15

代入演算子が右結合であることは理解しています。

たとえば x = y = z = 2(x = (y = (z = 2)))

ということで、以下のことを試してみました。

foo.x = foo = {a:1}

オブジェクトfooが値で作成され{a:1}、次にプロパティxが作成されfoo、オブジェクトへの参照になると予想していましたfoo

(これは、複数の代入ステートメントを 2 つの別個のステートメントに分割した場合に実際に起こることですfoo = {a:1};foo.x = foo;)

実際の結果は次のとおりです。

ReferenceError: foo が定義されていません(…)

それで、私は次のことを試しました:

var foo = {};
foo.x = foo = {a:1};

これで例外は発生しなくなりましたが、foo.x未定義です!

割り当てが期待どおりに機能しないのはなぜですか?


免責事項:「重複」の質問は、私が求めているものとは大きく異なるようです。問題は、割り当てで作成された変数がグローバルであり、varキーワードで作成された変数に並置されているためです。それはここでは問題ではありません。

4

3 に答える 3

10

結合性と評価順序には重要な違いがあります。

JavaScript では、代入演算子は右から左にグループ化されますが、オペランドは実際の代入が実行される前に左から右に評価されます(右から左に行われます)。次の例を検討してください。

var a = {};
var b = {};
var c = a;

c.x = (function() { c = b; return 1; })();

変数cは最初は を参照aしますが、代入の右側は に設定cされbます。どのプロパティが割り当てられますa.xb.x? 答えは、まだ を参照しているa.xときに、左側が最初に評価されるためです。ca

一般に、式x = yは次のように評価されます。

  1. 結果を評価xして覚えておいてください。
  2. 結果を評価yして覚えておいてください。
  3. ステップ 2 の結果をステップ 1 の結果に代入します (そして前者を式の結果として返しますx = y)。

のように、複数の割り当てがあるとどうなりx = (y = z)ますか? 再帰!

  1. 結果を評価xして覚えておいてください。
  2. 結果を評価y = zして覚えておいてください。これをする:
    1. 結果を評価yして覚えておいてください。
    2. 結果を評価zして覚えておいてください。
    3. ステップ 2.2 の結果をステップ 2.1 の結果に代入します (そして前者を式の結果として返しますy = z)。
  3. ステップ 2 の結果をステップ 1 の結果に代入します (そして前者を式の結果として返しますx = (y = z))。

少し編集した例を見てみましょう。

var foo = {};
var bar = foo;         // save a reference to foo
foo.x = (foo = {a:1}); // add parentheses for clarity

foo.xfooはに割り当てられる前に評価される{a:1}ため、xプロパティは元のオブジェクトに追加され{}ます (これは を調べることで確認できますbar)。

于 2015-12-01T18:34:36.507 に答える
0

素晴らしい質問です。ここで覚えておくべきことは、JavaScript はすべてにポインターを使用するということです。JavaScript ではメモリ アドレスを表す値にアクセスできないため、これを忘れがちです (この SO の質問を参照してください)。しかし、これを実現することは、JavaScript の多くのことを理解するために非常に重要です。

だから声明は

var foo = {};

メモリ内にオブジェクトを作成し、そのオブジェクトへのポインタを に割り当てますfoo。このステートメントを実行すると、次のようになります。

foo.x = foo = {a: 1};

プロパティxは実際にはメモリ内の元のオブジェクトに追加されfooますが、新しいオブジェクトへのポインタが割り当てられます{a: 1}. 例えば、

var foo, bar = foo = {};
foo.x = foo = {a: 1};

foobarが最初に同じオブジェクトを指している場合bar(元のオブジェクトを引き続き指している) は のように見えますが{x: {a: 1}}fooは単純に であることを示しています{a: 1}

では、なぜfooのように見えないの{a: 1, x: foo}でしょうか?

代入が右結合であるという点ではあなたの言う通りですが、インタプリタは依然として左から右に読み取ることも認識しておく必要があります。詳細な例を見てみましょう (いくつかのビットは抽象化されています)。

var foo = {};

さて、メモリ位置 47328 (またはその他) にオブジェクトを作成し、47328fooを指すポインターに割り当てます。

foo.x = ....

よし、現在メモリ位置 47328 をfoo指しているオブジェクトを取得し、それにプロパティを追加して、次に来るもののメモリ位置にx割り当てる準備をします。x

foo = ....

さて、ポインターfooをつかんで、次に来るもののメモリ位置に割り当てる準備をします.

{a: 1};

では、メモリの場所 47452 に新しいオブジェクトを作成します。次に、チェーンを上に戻ります。メモリの場所 47452 を指すように割り当てます。メモリの場所 47328 にあるオブジェクトのfooプロパティを、現在ポイントしているメモリの場所 47452 も指すように割り当てます。xfoo

要するに、簡単な方法はありません

var foo = {a: 1};
foo.x = foo;
于 2015-12-01T18:13:27.543 に答える