能夠在這裏看:http://leozdgao.me/renew-js-assignment/javascript
此文的目的是爲了解釋以下現象:java
var foo = { n: 1 }; var bar = foo; foo.x = foo = { n: 2 }; console.log(foo.x); // undefined
根據ECMA規範中的定義賦值運算符的產生式(production)以及運算過程以下:ide
The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:lua
Let lref be the result of evaluating LeftHandSideExpression.code
Let rref be the result of evaluating AssignmentExpression.對象
Let rval be GetValue(rref).ip
Throw a SyntaxError exception if the following conditions are all true:ci
Type(lref) is Reference is trueget
IsStrictReference(lref) is true博客
Type(GetBase(lref)) is Environment Record
GetReferencedName(lref) is either "eval" or "arguments"
Call PutValue(lref, rval).
Return rval.
其實第一次看這個部分我也仍是沒法理解開始列出的那個代碼中結果究竟是爲何,看了其餘博客其實也沒有講清楚,在StackOverflow上相關問題中有解釋到說是由於首先解析左邊表達式時肯定了引用的指向,而只看11.13.1的話,只有一句「獲取LeftHandSideExpression的計算結果」,因此我以爲要真正理解,只看11.13.1是不行,還須要瞭解下11.2.1關於Property Accessors的內容,從而知道這個「計算結果是如何獲得的」:
The production MemberExpression : MemberExpression [ Expression ] is evaluated as follows:
Let baseReference be the result of evaluating MemberExpression.
Let baseValue be GetValue(baseReference).
Let propertyNameReference be the result of evaluating Expression.
Let propertyNameValue be GetValue(propertyNameReference).
Call CheckObjectCoercible(baseValue).
Let propertyNameString be ToString(propertyNameValue).
If the syntactic production that is being evaluated is contained in strict mode code, let strict be true, else let strict be false.
Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.
關鍵點在於它的返回值,用一個栗子來解釋就是說:若是有表達式foo.x
,則它的返回值是一個指向foo對象x屬性的引用。
那麼在知道了這一點後,開始解釋上面的現象:
首先是兩個變量的聲明和初始化,var foo = { n: 1 }; var bar = foo;
,這個很好理解,就是foo和bar同時指向了一個相同的對象{ n: 1 }
。
接下來,對於表達式foo.x = foo = { n: 2 };
,咱們都知道它實際上等因而foo.x = (foo = { n: 2 })
。咱們開始應用上ECMA規範上的步驟,雖然賦值運算符具備右結合性,然而它首先作的是獲得表達式foo.x
的值,根據咱們對Property Accessors的解釋它返回一個指向對象{ n: 1}
的x成員的引用,須要注意的是,這個時候foo並無改變引用的指向。
繼續,開始計算右邊的結果,就是讓foo指向另外的一個對象{n: 2}
,返回值就是其右邊運算式(operand)的結果,即對象{n: 2}
這個容易理解。
那麼如今應該清楚了,賦值語句中foo.x
的結果是指向對象一成員x的引用,而下面的console.log(foo.x)
中的foo指向的是對象二,因此這裏foo.x
返回undefined
就理所固然了。
因此試着輸出對象一,即bar(由於它從始至終指向的是對象一):
{ n: 1, x: { n: 2 } }
若是有其餘見解,接受各類形式的補充和指正。