理解javascript中的连续赋值

之前在扒源码时经常看到类似的连续赋值操作:

 var a = b = 1; 

在某度搜了众多前辈的博客,总算对这骚操作有点眉目。

Case analysis

首先,javascript中连续赋值最典型案例是:

var a = {n: 1}; 
var b = a;
a.x = a = {n: 2}; 
console.log(a.x); //undefined 
console.log(b.x); //{n: 2} 

 

Analysis in short

var a = {n: 1}; //创建对象{n: 1},a指向对象{n: 1}
var b = a; //将 a中的地址传给b,即 b也指向对象{n: 1}
a.x = a = {n: 2}; 
/*
1)创建对象{n: 1}中的"x"属性
2)将a指向新对象{n: 2}
3) 将对象{n: 1}中的"x"指向新对象{n: 2}
*/
console.log(a.x); //undefined 
console.log(b.x); //{n: 2} 

 如果看懂了,下面可以直接忽略2333

 

 Analysis in long

我归纳了一下,要看懂这个问题要理解三个点:

  • 此处的a,b是引用类型

  • 在javascript中字段访问操作符"."的优先级高于赋值操作符"="

  • 出现多个赋值操作符"="时,运算顺序为从右向左

 

1. 此处的a,b是引用类型

引用类型是区别于基本类型的。javasript中的基本类型总共有5种,Undefined、Null、String、Number、Boolean。引用类型有Object,Array,Function,,,blabla~,简单说就是除了5种基本类型,可以说其它都为引用类型。

案例中的{n: 1}和{n: 2}都为Object类,即为引用类型。在javascript中当复制保存某个对象时,操作的是对象的引用,但在为对象添加属性时,操作的是实际的对象。即在第一行和第二行命令中a,b其实存储的是对象{n: 1}的引用(地址)。

这有点像C中的指针。如下图,案例第一行命令可以解析为,在堆(heap)中创建了一个对象{n: 1},另外在栈(stack)中创建了变量a,变量a的值为对象{n: 1}的地址,即图中的h[0]。第二行命令意思是再在栈中创建另一个变量b,变量b的值与变量a的值相等,即为对象{n: 1}的地址。

打个比方,你有一张去餐馆的地图,你把地图复制一份给你的基友,地图是多了一张,但餐馆的实际位置是没有改变的。

2. 运算顺序问题

这一节对应的是第二和第三个点。根据javascript中的运算符优先级 Operator precedence,可知:

  • 在javascript中字段访问操作符"."的优先级高于赋值操作符"="
  • 出现多个赋值操作符"="时,运算顺序为从右向左

因此在运行第三行命令时,分3步执行:

(1)先执行"a.x",此时a储存的仍然是h[0]地址,即指向对象{n: 1}。"a.x"相当于"h[0].x",即在对象{n: 1}中添加了属性"x",变为{n: 1; x: undefined},但是"x"还没赋值;注意,此时对象{n: 1; x: undefined}中的"x"属性是等待赋值的,操作挂起

 (2)再执行"a = {n: 2}",由于{n: 2}是个新对象,则程序在堆中再创建一个对象{n: 2},并且将变量a指向该对象,如图,此时a储存的即为h[1]的地址

(3)最后一步"a.x = a",(1)中被挂起的操作继续执行,对象{n: 1; x: undefined}中的"x"指向对象{n: 2}。运算完成。

3. 结果解释

在运行完上述命令后,变量a指向了新对象{n : 2};变量b 的地址没有发生改变,因而仍指向修改后的对象{n: 1;x: {n : 2}}。

var a = {n: 1}; 
var b = a; 
a.x = a = {n: 2}; 
console.log(a.x); //undefined 
console.log(b.x); //{n: 2} 

 

posted @ 2018-08-30 10:46  Gavin257  阅读(807)  评论(0编辑  收藏  举报