5

JSを理解したと思ったとき、私はこれに夢中になりました:

function Obj() {
    console.log('x: %s, o.x: %s', this.x++, this.o.x++);
}    
Obj.prototype.x = 1;
Obj.prototype.o = {x: 1};

期待される:

> new Obj
x: 1, o.x: 1
> new Obj
x: 1, o.x: 1
> new Obj
x: 1, o.x: 1

実際:

> new Obj
x: 1, o.x: 1
> new Obj
x: 1, o.x: 2
> new Obj
x: 1, o.x: 3

そのため、プロトタイプ プロパティが参照型の場合はすべてのインスタンスで共有されますが、非参照型の場合はインスタンスごとに再初期化されるようです。この仮説を確認するために、 (のstringように動作するnumber) やarray(のように動作する) など、他のいくつかのタイプをテストしましたobject

次のように、ctor でオブジェクト プロパティを再初期化すると、この落とし穴を回避できると判断しました。

function Obj() {
    this.o = {x: 1};
}

これは本当に非正統的なようです(プロパティを手動で再初期化する必要がありますが、それらが参照オブジェクトである場合のみ)。

何が起こっているのか、誰かが光を当てることができますか?

4

2 に答える 2

3

このように考えてください。値がどこから来るかに関係なく、操作しているオブジェクトを常に変更します。

これを初めて行う場合:

this.x++;

オブジェクトに直接プロパティがなかったためにxfromの値を取得していますが、はまだオブジェクトで動作しているため、値はそのオブジェクトに設定されています。その値は、将来の変更のためにインスタンスに直接適用されます。(直接プロパティがd になるまで、 はシャドウされます。)Obj.prototype.xxthis++thisprototype.xdelete

ただし、これを行うと:

this.o.x++

thisオブジェクトに対する唯一の操作は、 の検索ですooにもプロパティがないthisため、 に格納されているオブジェクトへの参照を取得しますObj.prototype.o。この時点では、thisオブジェクトの実際の変更はありません。

x参照が返されたら、Obj.prototype.oオブジェクトのプロパティを検索して変更します。

したがって、実際には非常に一貫しています。でthis.o.x++ルックアップを実行しましたthisが、ミューテーションはありません。突然変異は参照されたオブジェクトにあります。しかし ではthis.x++、オブジェクトを直接変更しています。


そうです、コンストラクターから作成されたすべてのインスタンス間で参照型を共有したくない場合は、参照型をインスタンスではなく直接インスタンスに配置する必要があります.prototype

于 2012-10-18T18:21:28.077 に答える
0

最初の回答が要点だと思いますが、少し違う角度から回答しています。

それを理解するための鍵は、オブジェクトへの割り当てに関するオブジェクトの動作です。

インスタンス プロパティがない場合、ルックアップはプロトタイプに戻ります。いつも。

ただし、割り当ては毎回新しいインスタンス プロパティを自動作成します。参照によってプロトタイプ化されたプロパティを新しいインスタンス プロパティに割り当てる場合、これらはどちらも同じものへの単なるポインタであるため、これは実際には問題になりません。また、インプレースで変更した場合、新しいインスタンス プロパティも作成されません。

function Obj(){
    //this.refArr resolves to what's in the prototype and that's what we change
    this.refArr.push(1);

    //now copy sliceArray and assign a new but identical array
    this.sliceArray = this.sliceArray.slice(0);

    //no length increase would mean a new instance property is created
    //... and then the assigment took place.
    this.sliceArray.push(1);
    console.log( 'ref:'+this.refArr.length +', sliceArray:'+this.sliceArray.length);
}
Obj.prototype.refArr = [];
Obj.prototype.sliceArray = [];
new Obj; new Obj;
//ref:1, sliceArray:1
//ref:2, sliceArray:1

そして、それは本質的にあなたがしていることの違いです. this.x = this.x + 1新しいプロパティを作成します。プロパティに割り当てるため、this.constructor.prototype.o新しいプロパティは作成されません。値を増やして this.o を this.o に割り当てても、プロトタイプと新しいプロパティ バージョンは同じオブジェクトを指しているだけなので、問題はありません。

于 2012-10-18T18:48:25.247 に答える