能够在这里看: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 } }
若是有其余见解,接受各类形式的补充和指正。