写在前面,本文让读者产生了误会。有这样一些缘由:git
a.x = a
未解释清楚。首先:本文用addr只是一个代称,表达的是该地址对应的那块内存。 关于2.3点就是本次更新的缘由github
a.x = a
赋值表达式先肯定左值(能够这样理解,若是不肯定我要去的地方,取到值又有什么用呢?左边的值在执行赋值以前就已经肯定了),而后再将右边表达式的返回值给到左值。那么a.x = a = {n : 2}
就是从左往右先肯定a.x
再肯定a
,而后将返回值从右往左依次赋值给左边。将题目改写成:
var a = {n: 1};
var b = a;
a.x = a = a.y = {n: 2};
//改写成这样,那么怎么肯定优先级呢?
复制代码
如今按照我文章的思路来:面试
addr(a.x) = addr(a) = addr(a.y) = addr({n : 2})
addr(a) = 0x100,
addr(a.x) = 0x101,
addr(a.y) = 0x102,
addr({n : 2}) = 0x888,
addr({n : 1}) = 0x999
复制代码
2.(从右往左)将右边的值赋值给左值,而后将其做为该赋值表达式的值返回:bash
1. 先执行:addr(a.y) = addr({n : 2}),将{n : 2}的地址值存放在addr(a.y)这个地址值对应的内存!中。
(别用箭头指,容易混淆,简单的当作赋值就行了。本文就是犯得这个错,致使没有说清楚。)
2. 而后将addr(a.y) = addr({n : 2})的右值做为该表达式的返回值N返回,在这里会做为下一个赋值表达式的右值。
3. 接着执行addr(a) = N,同上返回N。
4. 接着执行addr(a.x) = N,返回N。
而后咱们执行
console.log(a); // {n : 2}
console.log(a.x); // undefined
console.log(a.y); // undefined
console.log(b.x); // {n : 2}
console.log(b.y); // {n : 2}
复制代码
由于此时a(0x100)这块内存存的是对{n : 2}的引用,它会去0x888这块内存中找,这样确定找不着x和y,由于他们在0x999内存中。ide
而b在一开始var b = a的时候,就将a(0x100)这块内存中存的(0x999)拷贝过来。而y和x就在0x999对应的内存下。因此b找的到。函数
有这样一道面试题lua
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
alert(a.x); // undefined
alert(b.x); // [object, Object]
复制代码
一开始没有太好思路,或者说是没有想明白,通过一番折腾,算是整理清楚了思路,接下来会一一讲明白,但愿能对其余人有所帮助。es5
开始以前,须要清楚赋值表达式是怎么执行的。首先先明白什么是什么是右结合性和什么是赋值表达式:spa
赋值运算符是右结合性的,若是不知道,就请记住啦! 形如:翻译
A = B = C = D
等价于
A = (B = (C = D))
A = B
这就是一个赋值表达式,而且一个赋值表达式存在一个左值和一个右值,这可不是胡编乱造的,我们说话有理有据: 引用连接(11.13.1 Simple Assignment ( = ) )
The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:
- Let lref be the result of evaluating LeftHandSideExpression.
- Let rref be the result of evaluating AssignmentExpression.
- Let rval be GetValue(rref).
- Throw a SyntaxError exception if the following conditions are all true:
- Type(lref) is Reference is true
- IsStrictReference(lref) is true
- Type(GetBase(lref)) is Environment Record
- GetReferencedName(lref) is either "eval" or "arguments"
- Call PutValue(lref, rval).
- Return rval.
翻译过来:
大致来讲 赋值表达式:左边的表达式 = 赋值表达式 具体评判的步骤以下:
- 将比左边的表达式的值称为'lref'
- 将右边赋值表达式的值称为'rref'
- 将'rval' 做为GetValue(rref)'的返回值
- 若是下列状况为true就会报错
- balala~~~~
- 调用 PutValue(lref, rval)
- 返回'rval'
由上面能够清楚的知道表达式运算的流程:
(等等,这里有疑问,为何运算两遍右边?第一遍是计算右边表达式的(+ — * /)所获得的值,第二遍是返回这个结果值,也就是每一个表达式都有返回值!)
若是还没理解的话,不要紧,往下看,我会画图帮助理解。
有了以上两个知识点,下面来分析一下题目。
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
alert(a.x); // undefined
alert(b.x); // [object, Object]
复制代码
真正难以理解的是在第三句代码a.x = a = {n: 2}
下面我们开工吧!
第1、二句代码执行以后,内存图以下:
第三句代码先进行改写
a.x = a = {n: 2};
//先右结合性
a.x = (a = {n : 2})
//在计算等号左边的值
addr(a.x) = (addr(a) = {n : 2})
//在计算等号右边的值
addr(a.x) = ( addr(a) = value( {n : 2} ) )
复制代码
如图:
如上图来进行运算 addr(a.x) = ( addr(a) = value( {n : 2} ) )
(0x8889 <-- (0x0001 <-- (0x9999)))
一开始找到当前表达式左右左边的值,也就是他们地址值。而后依次将右值逐个放到对应的内存!
最后由于a的地址是指向0x9999的,且其不存x这个属性,故返回undefined。而b的地址指向里面存在一个保存{n:2}的地址的x属性,故返回的是对象。