17

さて、私はこれが何らかの方法で可能であるかを理解しようとしました。コードは次のとおりです。

a=function(text)
{
   var b=text;
   if (!arguments.callee.prototype.get)
      arguments.callee.prototype.get=function()
    {
         return b;
    }
    else
      alert('already created!');
}

var c=new a("test");  // creates prototype instance of getter
var d=new a("ojoj");  // alerts already created
alert(c.get())        // alerts test 
alert(d.get())        // alerts test from context of creating prototype function :(

ご覧のとおり、プロトタイプのゲッターを作成しようとしました。何のために?さて、あなたがこのようなものを書くなら:

a=function(text)
{
    var b=text;
    this.getText=function(){ return b}
}

...すべてがうまくいくはずですが、実際にはオブジェクトを作成するたびに、メモリを使用するgetText関数を作成します。同じことを行うプロトタイプ関数をメモリに配置したいと思います...何かアイデアはありますか?

編集:

クリストフから提供された解決策を試しましたが、今のところ唯一の既知の解決策のようです。コンテキストから値を取得するには ID 情報を記憶する必要がありますが、全体のアイデアは私にとっては素晴らしいものです :) ID は覚えておくべき唯一のものであり、他のすべては一度メモリに格納できます。実際、この方法で多くのプライベート メンバーを保存し、いつでも 1 つの ID だけを使用できます。実際、これは私を満足させます:)(誰かがより良い考えを持っていない限り)。

someFunc = function()
{
  var store = new Array();
  var guid=0;
  var someFunc = function(text)
  {
    this.__guid=guid;
    store[guid++]=text;
  }

  someFunc.prototype.getValue=function()
  {
    return store[this.__guid];
  }

  return someFunc;
}()

a=new someFunc("test");
b=new someFunc("test2");

alert(a.getValue());
alert(b.getValue());
4

8 に答える 8

31

JavaScript は従来、プロパティを非表示にするメカニズム (「プライベート メンバー」) を提供していませんでした。

JavaScript はレキシカル スコープであるため、コンストラクター関数を「プライベート メンバー」のクロージャーとして使用し、コンストラクターでメソッドを定義することにより、常にオブジェクトごとのレベルでこれをシミュレートできますが、これは、で定義されたメソッドでは機能しません。コンストラクターのプロトタイプ プロパティ。

もちろん、これを回避する方法はありますが、お勧めしません。

Foo = (function() {
    var store = {}, guid = 0;

    function Foo() {
        this.__guid = ++guid;
        store[guid] = { bar : 'baz' };
    }

    Foo.prototype.getBar = function() {
        var privates = store[this.__guid];
        return privates.bar;
    };

    Foo.prototype.destroy = function() {
        delete store[this.__guid];
    };

    return Foo;
})();

これにより、「プライベート」プロパティがインスタンスとは別の別のオブジェクトに保存されFooます。destroy()オブジェクトの処理が完了したら、必ず呼び出してください。そうしないと、メモリ リークが発生します。


edit 2015-12-01: ECMAScript6 は、たとえばWeakMapまたはできればSymbolを使用して、外部ストアの必要性を完全に回避することにより、手動のオブジェクト破壊を必要としない改良されたバリアントを可能にします。

var Foo = (function() {
    var bar = Symbol('bar');

    function Foo() {
        this[bar] = 'baz';
    }

    Foo.prototype.getBar = function() {
        return this[bar];
    };

    return Foo;
})();
于 2009-01-27T12:55:56.513 に答える
3

いくつかの ES6 テクノロジを採用している最新のブラウザでWeakMapは、GUID の問題を回避するために使用できます。これは IE11 以降で機能します。

// Scope private vars inside an IIFE
var Foo = (function() { 
    // Store all the Foos, and garbage-collect them automatically
    var fooMap = new WeakMap();

    var Foo = function(txt) { 
        var privateMethod = function() { 
            console.log(txt); 
        };
        // Store this Foo in the WeakMap
        fooMap.set(this, {privateMethod: privateMethod}); 
    } 

    Foo.prototype = Object.create(Object.prototype); 
    Foo.prototype.public = function() { 
        fooMap.get(this).p(); 
     } 
     return Foo; 
 }());

 var foo1 = new Foo("This is foo1's private method");
 var foo2 = new Foo("This is foo2's private method");
 foo1.public(); // "This is foo1's private method"
 foo2.public(); // "This is foo2's private method"

WeakMapFooは、削除されたり範囲外になったりした後は参照を保存しません。また、オブジェクトをキーとして使用するため、オブジェクトに GUID をアタッチする必要はありません。

于 2015-10-06T13:59:32.263 に答える
2

Christoph の回避策に大いに刺激を受けた後、いくつかの機能強化を提供する、わずかに変更されたコンセプトを思いつきました。繰り返しますが、このソリューションは興味深いものですが、必ずしも推奨されるわけではありません。これらの機能強化には、次のものが含まれます。

  • コンストラクターでセットアップを実行する必要がなくなりました
  • インスタンスにパブリック GUID を保存する必要がなくなりました
  • シンタックス シュガーを追加

基本的にここでの秘訣は、関連付けられたプライベート オブジェクトにアクセスするためのキーとしてインスタンス オブジェクト自体を使用することです。キーは文字列でなければならないため、通常、これはプレーン オブジェクトでは不可能です。しかし、式が を({} === {})返すという事実を利用して、これを達成することができましたfalse。つまり、比較演算子は一意のオブジェクト インスタンスを識別できます。

簡単に言うと、2 つの配列を使用して、インスタンスとそれに関連付けられたプライベート オブジェクトを維持できます。

Foo = (function() {
    var instances = [], privates = [];

    // private object accessor function
    function _(instance) {
        var index = instances.indexOf(instance), privateObj;

        if(index == -1) {
            // Lazily associate instance with a new private object
            instances.push(instance);
            privates.push(privateObj = {});
        }
        else {
            // A privateObject has already been created, so grab that
            privateObj = privates[index];
        }
        return privateObj;
    }

    function Foo() {
        _(this).bar = "This is a private bar!";
    }

    Foo.prototype.getBar = function() {
        return _(this).bar;
    };

    return Foo;
})();

_上記の機能に気付くでしょう。これは、プライベート オブジェクトを取得するためのアクセサ関数です。これは遅延して動作するため、新しいインスタンスで呼び出すと、その場で新しいプライベート オブジェクトが作成されます。

すべてのクラスのコードを複製したくない場合は_、ファクトリ関数内にラップすることでこれを解決できます。

function createPrivateStore() {
    var instances = [], privates = [];

    return function (instance) {
        // Same implementation as example above ...
    };
}

これで、クラスごとに 1 行に減らすことができます。

var _ = createPrivateStore();

繰り返しになりますが、必要に応じて破棄関数を実装して呼び出さないと、メモリ リークが発生する可能性があるため、このソリューションの使用には細心の注意を払う必要があります。

于 2013-12-11T07:43:55.617 に答える
2

プロトタイプのメソッドは、javascript に存在するため、「プライベート」メンバーにアクセスできません。ある種の特権アクセサが必要です。getをレキシカルに参照できる場所を宣言しているため、作成時bの状態が常に返さbれます。

于 2009-01-27T12:36:08.597 に答える
0

個人的には、GUID を使用したソリューションはあまり好きではありません。これは、開発者がストアに加えて GUID を宣言し、コンストラクターでインクリメントする必要があるためです。大規模な JavaScript アプリケーションでは、開発者が忘れてしまう可能性があり、エラーが発生しやすくなります。

コンテキスト(これ)を使用してプライベートメンバーにアクセスできるという事実のため、ピーターの答えがかなり気に入っています。しかし、非常に気になる点の 1 つは、プライベート メンバーへのアクセスが ao(n) の複雑さで行われるという事実です。実際、配列内のオブジェクトのインデックスを見つけることは線形アルゴリズムです。10000 回インスタンス化されるオブジェクトにこのパターンを使用するとします。次に、プライベート メンバーにアクセスするたびに、10000 のインスタンスを反復処理する場合があります。

ao(1) の複雑さでプライベート ストアにアクセスするには、guid を使用する以外に方法はありません。しかし、GUID の宣言とインクリメントに煩わされず、コンテキストを使用してプライベート ストアにアクセスするために、Peters ファクトリ パターンを次のように変更しました。

createPrivateStore = function () {
var privates = {}, guid = 0;

return function (instance) {
    if (instance.__ajxguid__ === undefined) {
        // Lazily associate instance with a new private object
        var private_obj = {};
        instance.__ajxguid__ = ++guid;
        privates[instance.__ajxguid__] = private_obj;
        return private_obj;
    }

    return privates[instance.__ajxguid__];
}

}

ここでの秘訣は、 ajxguidプロパティを持たないオブジェクトがまだ処理されていないことを考慮することです。実際、ストアに初めてアクセスする前にプロパティを手動で設定することもできますが、魔法のような解決策はないと思います。

于 2014-04-17T13:16:43.953 に答える