Skip to content

JS解惑-连等表达式 #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
sunmaobin opened this issue Nov 20, 2017 · 0 comments
Open

JS解惑-连等表达式 #39

sunmaobin opened this issue Nov 20, 2017 · 0 comments
Assignees
Milestone

Comments

@sunmaobin
Copy link
Owner

sunmaobin commented Nov 20, 2017

JS解惑-连等表达式

问题

如下3个表达式的区别:

//sample1
var str = 123;
var str1 = 123;
var str2 = 123;

//sample2
var str = 123,str1 = 123,str2 = 123;

//sample3
var str = str1 = str2 = 123;

答案

//`sample1` = `sample2`,区别仅仅是代码风格而已;

//sample2
var str = 123,str1 = 123,str2 = 123; //str,str1,str2 全部为局部变量

//sample3
var str = str1 = str2 = 123; //str 局部变量,str1,str2 为全局变量

最新补充于:2018年3月18日

关于连等表达式多啰嗦一些

连等表达式执行过程说明

  1. 先解析连等表达式,进行变量提升(hoisting)
    • 如果是局部变量,不存在则先定义
  2. 将连等式最后一个值,同时赋给前面的其它变量;
    • 如果最后一个值是对象,则是将这个对象先在内存中开辟一个位置(匿名),然后将这个指针指向前面的所有变量。

关于以上第1步的详细解释

比如:

var str=str1=str2=123;

解析过程如下:

  1. 先找到表达式中的变量:strstr1str2
  2. 进行变量提升,并定义:
    • var str,表示这是一个局部变量,则直接在当前执行函数中定义变量str,默认值:undefined

注意: str1str2,由于他俩前面并没有直接有 var 符号,但是又得为它们赋值,这时候JS引擎赋值时,就会把他们挂到全局对象 window 对象上,因为 window 对象已经存在,所以不会在定义了;而对象的属性,只有在赋值时,才会动态在堆内存中追加。所以,var1var2 也不会创建。

记住:变量提升创建的,只包含:基本类型、对象和声明的函数,不包含对象上的属性。

接下来就是一步步赋值的过程了,见下面第2步的解释。

关于以上第2步的详细解释

比如:

var str=str1=str2=123;

并不是: 将123先赋值给str2,再将str2赋值给str1,再将str1赋值给str;
而是: 将123赋值给str2,再将123赋值给str1,再将123赋值给str;

也就是上方的连等式可以理解为:

var str;
str = 123;
str1 = 123
str2 = 123;

复杂示例

请大家给出如下表达式的执行结果:

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

大家先自己给出一个答案,然后看看下面的分析跟你想的一样不?

详细分析

第一步:先将代码中的变量提升,不存在的变量先定义。

  1. 先找出以上代码片段中的所有变量
    • a
    • b
  2. 定义变量
    • var a:定义局部变量a,默认值 undefined
    • var b:定义局部变量b,默认值 undefined

于是代码片段变为:

//第一步做的事情
var a;  
var b;

//第二步开始的地方
a = {n:1}; 
b = a;

a.x = a = {n:2};  
console.log('a.x=',a.x);//?
console.log('b.x=',b.x);//?

第二步:一步步执行代码片段并赋值。

  1. a = {n:1} 将a赋值为一个对象 {n:1},这里发生了什么事情?

  2. b = a 将a对象赋值给b,我们知道,对象赋值其实就是指针的指向问题,就是b的指针也指向了a的值;

  3. a.x = a = {n:2},这一步我们按照上方的原理(并不是依次从右往左执行),把它拆解一下:

    • a.x = {n:2};
    • a = {n:2}
  4. 先看前一步,a.x = {n:2},其实就是在a的堆内存中,再额外增加一个属性x,并且指向新的对象,如下:

    • 执行完这一步,大家能看到,其实这时候:a 和 b 还是指向同一个对象,只是那个对象多了一个属性x而已。
    • 这时候:a.x = b.x 都等于 {n:2}
  5. 再看后一步,a = {n:2},就是将a的指针变化了,指向这个新的对象,如下:

    • 这时候:a = b.x 都等于 {n:2}(看蓝色的箭头指向同一个)
    • 而这时候a指针对应的对象中 {n:2},根本不存在x这个属性,所以 a.x=undefined;

最终答案

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

其实本题最容易绕晕的2个地方:

  1. 很多人把连等当成从右往左一个变量给另一个变量赋值!其实不是;
  2. 最后 {n:2} 是个新的对象,注意的地方是对象赋值,一定是一个新的地址了。

参考

(全文完)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant