由ES规范学JavaScript(二):深入理解“连等赋值”问题
var foo={rzx:1}
var bar =foo;
foo.x=foo={rzx:100}
console.log(foo.x)
console.log(bar.x)

有这样一个热门问题:
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
alert(a.x); // --> undefined
alert(b.x); // --> {n: 2}
其实这个问题很好理解,关键要弄清下面两个知识点:
JS引擎对赋值表达式的处理过程
赋值运算的右结合性
一. 赋值表达式
形如
A = B
的表达式称为赋值表达式。其中A和B又分别可以是表达式。B可以是任意表达式,但是A必须是一个左值。
所谓左值,就是可以被赋值的表达式,在ES规范中是用内部类型引用(Reference)描述的。例如:
表达式
foo.bar可以作为一个左值,表示对foo这个对象中bar这个名称的引用;变量
email可以作为一个左值,表示对当前执行环境中的环境记录项envRec中email这个名称的引用;同样地,函数名
func可以做左值,然而函数调用表达式func(a, b)不可以。
那么JS引擎是怎样计算一般的赋值表达式 A = B的呢?简单地说,按如下步骤:
计算表达式A,得到一个引用
refA;计算表达式B,得到一个值
valueB;将
valueB赋给refA指向的名称绑定;返回
valueB。
二. 结合性
所谓结合性,是指表达式中同一个运算符出现多次时,是左边的优先计算还是右边的优先计算。
赋值表达式是右结合的。这意味着:
A1 = A2 = A3 = A4
等价于
A1 = (A2 = (A3 = A4))
三. 连等的解析
好了,有了上面两部分的知识。下面来看一下JS引擎是怎样运算连等赋值表达式的。
以下面的式子为例:
Exp1 = Exp2 = Exp3 = Exp4
首先根据右结合性,可以转换成
Exp1 = (Exp2 = (Exp3 = Exp4))
然后,我们已经知道对于单个赋值运算,JS引擎总是先计算左边的操作数,再计算右边的操作数。所以接下来的步骤就是:
计算Exp1,得到Ref1;
计算Exp2,得到Ref2;
计算Exp3,得到Ref3;
计算Exp4,得到Value4。
现在变成了这样的:
Ref1 = (Ref2 = (Ref3 = Value4))
接下来的步骤是:
将Value4赋给Exp3;
将Value4赋给Exp2;
将Value4赋给Exp1;
返回表达式最终的结果Value4。
注意:这几个步骤体现了右结合性。
总结一下就是:
先从左到右解析各个引用,然后计算最右侧的表达式的值,最后把值从右到左赋给各个引用。
四. 问题的解决
现在回到文章开头的问题。
首先前两个var语句执行完后,a和b都指向同一个对象{n: 1}(为方便描述,下面称为对象N1)。然后来看
a.x = a = {n: 2};
根据前面的知识,首先依次计算表达式a.x和a,得到两个引用。其中a.x表示对象N1中的x,而a相当于envRec.a,即当前环境记录项中的a。所以此时可以写出如下的形式:
[[N1]].x = [[encRec]].a = {n: 2};
其中,[[]]表示引用指向的对象。
接下来,将{n: 2}赋值给[[encRec]].a,即将{n: 2}绑定到当前上下文中的名称a。
接下来,将同一个{n: 2}赋值给[[N1]].x,即将{n: 2}绑定到N1中的名称x。
由于b仍然指向N1,所以此时有
b <=> N1 <=> {n: 1, x: {n: 2}}
而a被重新赋值了,所以
a <=> {n: 2}
并且
a === b.x
五. 最后的最后
如果你明白了上面所有的内容,应该会明白a.x = a = {n:2};与b.x = a = {n:2};是完全等价的。因为在解析a.x或b.x的那个时间点。a和b这两个名称指向同一个对象,就像C++中同一个对象可以有多个引用一样。而在这个时间点之后,不论是a.x还是b.x,其实早就不存在了,它已经变成了那个内存中的对象.x了。
最后用一张图表示整个表达式的运算过程:
六. 参考文档
由ES规范学JavaScript(二):深入理解“连等赋值”问题的更多相关文章
- 从头开始学JavaScript (二)——变量及其作用域
原文:从头开始学JavaScript (二)--变量及其作用域 一.变量 ECMAscript变量是松散型变量,所谓松散型变量,就是变量名称可以保存任何类型的数据,每个变量仅仅是一个用于保存值的占位符 ...
- Android OpenGL ES和OpenGL一起学(二)------理解Viewport(视口)和坐标系Android OpenGL ES篇(转帖)
来自:http://www.cnblogs.com/xiaobo68688/archive/2011/12/01/2269985.html 首先我们在屏幕中心显示一个矩形,效果如图: // 代 ...
- 从零开始学JavaScript二(基本概念)
基本概念 一.区分大小写 在ECMAScript中的一切(变量.函数名.操作符)都是区分大小写的. 如变量名test和Test分别表示两个不同的变量, 二.标识符 所谓标识符,就是指变量.函数.属性的 ...
- 从头开始学JavaScript (十二)——Array类型
原文:从头开始学JavaScript (十二)--Array类型 一.数组的创建 注:ECMAscript数组的每一项都可以保存任何类型的数据 1.1Array构造函数 var colors = ne ...
- 从头开始学JavaScript (五)——操作符(二)
原文:从头开始学JavaScript (五)--操作符(二) 一.乘性操作符 1.乘法:* 乘法操作符的一些特殊规则: 如果操作数都是数值,按照常规的乘法计算,如果乘积超过了ECMAscri ...
- 怎么学JavaScript?
作者:小不了链接:https://zhuanlan.zhihu.com/p/23265155来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 鉴于时不时,有同学私信问我( ...
- 统一回复《怎么学JavaScript?》
作者:小不了链接:https://zhuanlan.zhihu.com/p/23265155来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 鉴于时不时,有同学私信问我( ...
- 从头开始学JavaScript (十)——垃圾收集
原文:从头开始学JavaScript (十)--垃圾收集 一.垃圾收集 1.1javascript垃圾收集机制: 自动垃圾收集,执行环境会负责管理代码执行过程中的使用的内存.而在C和C++之类的语言中 ...
- 从头开始学JavaScript (六)——语句
原文:从头开始学JavaScript (六)--语句 一.条件分支语句:if 基本格式: if (<表达式1>){ <语句组1>}else if (<表达式2> ...
随机推荐
- MongoDB入门_shell基本操作
使用shell客户端连接mongoDB数据库 [root@localhost mongodb_simple]# ./bin/mongo /admin mongoDB的简单基本操作 1. mongoDB ...
- 四、Vue CLI-异步请求(axios)
一.模块的安装 npm install axios --save #--save可以不用写 如图: 二.配置main.js import axios from 'axios' Vue.prototyp ...
- Java Script入门
学习来源:https://www.runoob.com/js/js-tutorial.html JavaScript 教程 JavaScript 是 Web 的编程语言. 所有现代的 HTML 页面都 ...
- Access数据库删除重复记录,只保留一条记录的做法
Access数据库删除重复记录,只保留一条记录的做法: 只保留id最小的记录方法: delete from [表名] where id not in (select min(id) from [表名] ...
- 如何配置SQL Server数据库远程连接
本地数据库(SQL Server 2012或以上) 连接外网服务器的数据库,外网的服务器端需要做如下配置: 1.首先是要打开 数据的配置管理工具 2.配置相关的客户端协议,开启TCP/IP 3.数据库 ...
- 【CF461B】Appleman and Tree
题目大意:给定一棵 N 个节点的有根树,1 号节点为根节点,每个点有两种颜色(黑.白),现给出树上每个节点的颜色,求有多少种分割树的方式,使得分割出的每个联通块中有且仅有一个黑点. 题解:树形dp 由 ...
- 移动端h5模拟长按事件
为啥写这篇文章 最近接了个需求,要求长按某个标签显示删除一个悬浮的删除按钮.这个需求其实在app上很常见,但是在移动端h5中,我们没有长按的事件,所以就需要自己模拟这个事件了. 大概效果如下: ps: ...
- thinkphp5杂谈--模板
一种新型开源模板 http://www.h-ui.net/H-ui.admin.shtml 下载页面代码 除了curl以外还可以借助 仿站小工具V7.0,操作示意图
- electron 点击事件无效
用CSS的 -webkit-app-region: drag;设置窗口可以移动后,点击事件无效 解决办法暂时不知道, 给点击的按钮加 -webkit-app-region: no-drag; 就可以点 ...
- Nmap使用及常见的参数选项
概要 当Nmap不带选项运行时,该选项概要会被输出,最新的版本在这里http://www.insecure.org/nmap/data/nmap.usage.txt.它有助于人们记住最常用的选项,但不 ...