首页 技术 正文
技术 2022年11月15日
0 收藏 820 点赞 3,835 浏览 2188 个字

原文链接

千万不要在JS中使用连等赋值操作

 

目录

  • 来源1来源2)例子:

    var a = {n:1};
    a.x = a = {n:2};
    console.log(a.x); // 输出?

    答案是:

    console.log(a.x); // undefined
    console.log(a.x); // undefined
    console.log(a.x); // undefined

    不知道各位有没有答对,至少我是答错了。

    遂借此机会好好看看JS连等赋值是怎么回事

    赋值顺序?

    假设有一句代码: A=B=C; ,赋值语句的执行顺序是从右至左,所以问题在于:

    是猜想1: B = C; A = C; ?

    还是猜想2: B = C; A = B;  ?

    我们都知道若两个对象同时指向一个对象,那么对这个对象的修改是同步的,如:

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

    所以可以根据这个特性来测试连续赋值的顺序。

    按照猜想1,把C换成具体的对象,可以看到对a的修改不会同步到b上,因为在执行第一行和第二行时分别创建了两个 {n:1} 对象。如:

    var b={n:1};
    var a={n:1};
    a.n=0;
    console.log(b);//Object {n: 1}

    再按照猜想2,把C换成具体的对象,可以看到对a的修改同步到了b,因为a和b同时引用了一个对象,如:

    var b={n:1};
    var a=b;
    a.n=0;
    console.log(b);//Object {n: 0}

    测试真正的连等赋值:

    var a,b;
    a=b={n:1};
    a.n=0;
    console.log(b);//Object {n: 0}

    可以看到是符合猜想2的,如果有人觉得这个测试不准确可以再来测试,使用ECMA5的setter和getter特性来测试。

    首先setter和getter是应用于变量名的,而不是变量真正储存的对象,如下:

    【转】千万不要在JS中使用连等赋值操作

    Object.defineProperty(window,"obj",{
    get:function(){
    console.log("getter!!!");
    }
    });
    var x=obj;
    obj;//getter!!! undefined
    x;//undefined

    【转】千万不要在JS中使用连等赋值操作

    可以看到只有obj输出了“getter!!!”,而x没有输出,用此特性来测试。

    连等赋值测试2:

    Object.defineProperty(window,"obj",{
    get:function(){
    console.log("getter!!!");
    }
    });
    a=b=obj;//getter!!! undefined

    【转】千万不要在JS中使用连等赋值操作

    通过getter再次证实,在A=B=C中,C只被读取了一次。

    所以,连等赋值真正的运算规则是  B = C; A = B;  即连续赋值是从右至左永远只取等号右边的表达式结果赋值到等号左侧。

    连续赋值能拆开写么?

    通过上面可以看到连续赋值的真正规则,那么再回归到文章开头的那个案例,如果按照上述规则将连续赋值拆开会发现结果不一样了,如:

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

    所以连续赋值语句虽然是遵从从右至左依次赋值的规则但依然不能将语句拆开来写,至于为什么

    我猜测:js内部为了保证赋值语句的正确,会在一条赋值语句执行前,先把所有要赋值的引用地址取出一个副本,再依次赋值。

    所以我认为这段代码  a.x=a={n:2};  的逻辑是:

    1、在执行前,会先将a和a.x中的a的引用地址都取出来,此值他们都指向{n:1}

    2、在内存中创建一个新对象{n:2}

    3、执行a={n:2},将a的引用从指向{n:1}改为指向新的{n:2}

    4、执行a.x=a,此时a已经指向了新对象,而a.x因为在执行前保留了原引用,所以a.x的a依然指向原先的{n:1}对象,所以给原对象新增一个属性x,内容为{n:2}也就是现在a

    5、语句执行结束,原对象由{n:1}变成{n:1,x:{n:2}},而原对象因为无人再引用他,所以被GC回收,当前a指向新对象{n:2}

    6、所以就有了文章开头的运行结果,再执行a.x,自然就是undefined了

    上述过程按序号图示:

    【转】千万不要在JS中使用连等赋值操作

    【转】千万不要在JS中使用连等赋值操作

    按照上述过程可以看出旧的a.x和新的a都指向新创建的对象{n:2},所以他们应该是全等的。

    测试:

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

    【转】千万不要在JS中使用连等赋值操作

    因为我们增加了var b=a,即将原对象增加了一条引用,所以在上述第5步时不会被释放,证实了上面的结论。

    后记

    通过这次了解了连续赋值的特点,再回过头看文章标题,似乎应该叫:

    尽量不要使用JS的连续赋值操作,除非真的了解它的内部机制及可能会产生的后果。

    (完)

    原文链接-http://www.cnblogs.com/xxcanghai/p/4998076.html

    如果您认为本文对得起您所阅读他所花的时间,欢迎点击右下角↘ 推荐。您的支持是我继续写作最大的动力,谢谢

    作者:小小沧海 
    出处:http://www.cnblogs.com/xxcanghai/ 
    本文地址:http://www.cnblogs.com/xxcanghai/ 
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

上一篇: CSS中的BFC详解
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,497
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,910
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,744
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,498
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,136
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,300