由ES范例学JavaScript(二):深切明白“连等赋值”题目

有如许一个热点题目:

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的呢?简朴地说,按以下步骤:

  1. 盘算表达式A,获得一个援用refA

  2. 盘算表达式B,获得一个值valueB

  3. valueB赋给refA指向的称号绑定;

  4. 返回valueB

二. 连系性

所谓连系性,是指表达式中同一个运算符涌现屡次时,是左侧的优先盘算照样右边的优先盘算。

赋值表达式是右连系的。这意味着:

A1 = A2 = A3 = A4

等价于

A1 = (A2 = (A3 = A4))

三. 连等的剖析

好了,有了上面两部分的学问。下面来看一下JS引擎是如何运算连等赋值表达式的。

以下面的式子为例:

Exp1 = Exp2 = Exp3 = Exp4

起首依据右连系性,能够转换成

Exp1 = (Exp2 = (Exp3 = Exp4))

然后,我们已晓得关于单个赋值运算,JS引擎老是先盘算左侧的操作数,再盘算右边的操作数。所以接下来的步骤就是:

  1. 盘算Exp1,获得Ref1;

  2. 盘算Exp2,获得Ref2;

  3. 盘算Exp3,获得Ref3;

  4. 盘算Exp4,获得Value4。

如今变成了如许的:

Ref1 = (Ref2 = (Ref3 = Value4))

接下来的步骤是:

  1. 将Value4赋给Exp3;

  2. 将Value4赋给Exp2;

  3. 将Value4赋给Exp1;

  4. 返回表达式终究的效果Value4。

注重:这几个步骤表现了右连系性。

总结一下就是:

从左到右剖析各个援用,然后盘算最右边的表达式的值,末了把值从右到左赋给各个援用。

四. 题目的处理

如今回到文章开首的题目。

起首前两个var语句实行完后,ab都指向同一个对象{n: 1}(为轻易形貌,下面称为对象N1)。然后来看

a.x = a = {n: 2};

依据前面的学问,起首顺次盘算表达式a.xa,获得两个援用。个中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.xb.x的谁人时候点ab这两个称号指向同一个对象,就像C++中同一个对象能够有多个援用一样。而在这个时候点以后,不论是a.x照样b.x,实在早就不存在了,它已变成了谁人内存中的对象.x了。

末了用一张图示意全部表达式的运算历程:

《由ES范例学JavaScript(二):深切明白“连等赋值”题目》

六. 参考文档

11.13.1 Simple Assignment ( = )

    原文作者:manxisuo
    原文地址: https://segmentfault.com/a/1190000004224719
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞