JavaScript中包含两个赋值运算符的复合表达式的求值过程
The evaluation process of a compound expression containing two assignment operators in JavaScript
这是一个包含两个赋值运算符的复合表达式:
var a = {n: 1};
var b = a;
a.x = a = {m: 2};
a; // => {m: 2}
b; // => {n: 1, x: {m: 2}}
棘手的部分是第三行:
a.x = a = {m: 2};
恕我直言,赋值运算符=
是右结合的,所以表达式的嵌套结构是:
a.x = (a = {m: 2});
但是ES5中的求值顺序总是从左到右,根据ES5 Annex D。
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:
....omitted intentionally to save space
- PutValue(lref, rval).
- Return rval.
所以我对从左到右求值顺序的理解是:
- 先评估
a.x
并 return 参考 lref1
求a = {m: 2}
得到rref1
,因为也是赋值表达式,我们再开始程序(像递归)
2.1。首先评估 a
并 return 对其进行参考 lref2
2.2。评估 {m: 2}
和 return 对象 {m: 2}
为 rref2
2.3。设rval2
= GetValue(rref2)
,所以rval2
也是对象{m: 2}
2.4。 PutValue(lref2, rval2)
,因此 a
将重新绑定对象 {m: 2}
而不是 {n: 1}
2.5。 returnrval2
,即对象{m: 2}
为rref1
(不是引用类型,而是对象)
设rval1
=GetValue(rref1)
,也正是对象{m: 2}
PutValue(lref1, rval1)
,所以lref1
指向的内存地址就是{m: 2}
。并且 b.x
仍然引用此地址,并且 b
将被更新。
这个过程符合 ES5 规范并且很好地解释了结果。
我的问题是:
上面这个评价顺序是真是假?如果为假,还有其他解释吗?
如何正确理解ES5中的Reference Specification Type?它只是一个指向特定内存地址的中间指针吗?
是的,您对运算符顺序的理解似乎是正确的。
A Reference consists of three components, the base value, the referenced name and the Boolean valued strict reference flag. The base value is either undefined, an Object, a Boolean, a String, a Number, or an environment record (10.2.1).
从 属性 访问创建引用的过程定义在 11.2.1:
- Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.
因此引用 lref1
包含 a
的 对象值 (连同引用的名称字符串 "x"
),您最初使用 {n: 1}
创建。引用不关心它来自的变量;它只关心创建时提供的基本值和参考名称。
改变 a
持有的值对引用 lref1
持有的基值没有任何影响。 lref1
继续保留 a
的原始值(即 {n: 1}
对象),而不管 a
在创建 lref1
后做了什么。
简而言之,从表达式 a.x
创建的引用在创建引用后就不再与变量 a
有任何关系。相反,引用只知道 a
在创建引用时持有的值。
这是一个包含两个赋值运算符的复合表达式:
var a = {n: 1};
var b = a;
a.x = a = {m: 2};
a; // => {m: 2}
b; // => {n: 1, x: {m: 2}}
棘手的部分是第三行:
a.x = a = {m: 2};
恕我直言,赋值运算符=
是右结合的,所以表达式的嵌套结构是:
a.x = (a = {m: 2});
但是ES5中的求值顺序总是从左到右,根据ES5 Annex D。
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: ....omitted intentionally to save space
- PutValue(lref, rval).
- Return rval.
所以我对从左到右求值顺序的理解是:
- 先评估
a.x
并 return 参考lref1
求
a = {m: 2}
得到rref1
,因为也是赋值表达式,我们再开始程序(像递归)2.1。首先评估
a
并 return 对其进行参考lref2
2.2。评估
{m: 2}
和 return 对象{m: 2}
为rref2
2.3。设
rval2
=GetValue(rref2)
,所以rval2
也是对象{m: 2}
2.4。
PutValue(lref2, rval2)
,因此a
将重新绑定对象{m: 2}
而不是{n: 1}
2.5。 return
rval2
,即对象{m: 2}
为rref1
(不是引用类型,而是对象)设
rval1
=GetValue(rref1)
,也正是对象{m: 2}
PutValue(lref1, rval1)
,所以lref1
指向的内存地址就是{m: 2}
。并且b.x
仍然引用此地址,并且b
将被更新。
这个过程符合 ES5 规范并且很好地解释了结果。
我的问题是:
上面这个评价顺序是真是假?如果为假,还有其他解释吗?
如何正确理解ES5中的Reference Specification Type?它只是一个指向特定内存地址的中间指针吗?
是的,您对运算符顺序的理解似乎是正确的。
A Reference consists of three components, the base value, the referenced name and the Boolean valued strict reference flag. The base value is either undefined, an Object, a Boolean, a String, a Number, or an environment record (10.2.1).
从 属性 访问创建引用的过程定义在 11.2.1:
- Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.
因此引用 lref1
包含 a
的 对象值 (连同引用的名称字符串 "x"
),您最初使用 {n: 1}
创建。引用不关心它来自的变量;它只关心创建时提供的基本值和参考名称。
改变 a
持有的值对引用 lref1
持有的基值没有任何影响。 lref1
继续保留 a
的原始值(即 {n: 1}
对象),而不管 a
在创建 lref1
后做了什么。
简而言之,从表达式 a.x
创建的引用在创建引用后就不再与变量 a
有任何关系。相反,引用只知道 a
在创建引用时持有的值。