離人涙

文章 评论 浏览 31

離人涙 2022-05-04 13:56:45

有一个网站可以将 JavaScript 代码的执行过程,用可视化的方式呈现出现。具体链接如下:tylermcginnis

从可视化的执行过程来看,并没有之前上面答案所说的对象增加 x 属性的这个过程,也即 { n: 1 } => { n: 1, x: undefined },而是最后直接变成 { n: 1, x: { n: 2 } }

const a = {};
const b = 1;
a.x = b;

第三行代码 a.x = b; 在执行的过程中,会执行一次左查询以及一次右查询。这里所说的“左” / “右”是把 = 操作符作为参照物,a.x 执行左查询是为了弄清楚在 a 对象上是否存在 x 属性,如果存在,那么 a.x = b; 语句执行的是更新属性的操作;反之,则是新增属性的操作。如果在查询的过程中,发现 a 不存在,则引擎会报错。b 执行右查询是为了获取 b 的值。如果在查询的过程中,发现 b 不存在,引擎可能报错,也可能不报错。至于在赋值的过程中,是否执行左查询或者右查询,关键是看 = 的左右两边是否存在变量

这个问题考察的知识点主要有以下这些:

  • . 的优先级高于 = 的优先级
  • = 具有右结合性(执行的方向是从右往左,先执行 = 右边的表达式,然后把结果赋值给 = 左边的表达式,从这里可以得出 = 属于二元操作符),多个 = 的执行过程,可以类比成"递归"的过程
let a = { n: 1 };
const b = a;

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

执行完第一行以及第二行代码之后,变量 a 和 常量 b 指向同一块内存地址(对象 { n: 1 } 在内存里面的内存地址)。换句话说,a 现在是 b 的别名;反之亦然

在执行第三行代码之前,你要知道 a.x = a = { n: 2 } 里面包含两种操作符(.=)。也正是由于 . 的优先级高于 = 的优先级,所以会首先执行 a.x。不过在执行 a.x 的过程中,会执行一次“左”查询。经过左查询之后,发现对象 a 没有 x 属性(在这里你可以认为代码已经变成 ({ n: 1 }).x 或者 b.x),然后会再去执行第一个 = 操作符。由于 = 具有右结合性,所以会先去执行 a = { n: 2 }。在执行的过程中,发现 a = { n: 2 } 是一个普通的赋值操作。而且也正是因为 = 右边是一个对象字面量,所以在这里是不存在右查询以及表达式的计算过程。不过在把 { n: 2 } 赋给变量 a 之前,需要对变量 a 执行一次左查询。经过左查询之后,发现变量 a 已经被声明(假如发现变量 a 没有被声明,在非严格模式下,对它赋值的这个操作不会导致引擎报错),所以会继续把 { n: 2 } 赋值给变量 a。之后会把 a = { n: 2 } 语句的返回结果,作为第一个 = 右边的表达式。所以第三行代码变成 ({ n: 1 }).x = { n: 2 } 或者 b.x = { n: 2}。如果没有第二行代码 const b = a;,在执行完第三行代码之后,对象 { n: 1, x: { n: 2} } 所占据的内存会被 GC 回收。补充一句,假设第三行代码就只有 a.x 的话,那么第三行代码的执行过程就结束啦。

至于想搞清楚自己到底有没有理解这个,可以尝试想一下:如果 . 的优先级低于 = 的优先级,上述代码的执行过程是怎样的?

let a = { n: 1 };
const b = a;

// `.` 的优先级低于 `=` 的优先级
a.x = a = { n: 2 };
console.log(a); // 报错

简单的分析一下,a.x = a = { n: 2 }; 这段代码,最后会演变成 (a = { n: 2 }, x = (a = { n: 2 }), a.(x = (a = { n: 2 })))。简化一下会变成这样:(a = { n: 2 }, x = a, a.{ n: 2 })

第 53 题:输出以下代码的执行结果并解释为什么

更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文