解答上个沸点的问题: 为什么 JS 中 `+[] === 0` 😁

269 阅读2分钟

因为沸点有字数限制, 所以用文章来解读

blog 授权: creativecommons.org/licenses/by…

我会从 ECMAScript 规范的层面来解读这个问题.

首先是 + 这个操作符, 来看一下规范是怎么说的

The unary + operator converts its operand to Number type.

译: unary + 操作符转换其的操作对象到 Number 类型.

在随后的算法中调用了 ToNumber 这个抽象行为.

来看一下 ToNumber 做了什么

其将参数转换成 Number 类型根据该表:

显然 [] 是一个对象, 根据表格中对于 Object 的约定, 第一步去调用 ToPrimitive([], hint Number)

来看一下 ToPrimitive 做了什么

其会调用对象上的 @@toPrimitive 方法, 注意一点, Array.prototype 没有实现 @@toPrimitive 方法, Date.prototype 实现了, 感兴趣可以看一下.

所以在 ToPrimitive 算法的 2.g 中调用了 OrdinaryToPrimitive([], hint Number)

来看一下 OrdinaryToPrimitive 做了什么

在该算法中, 会根据 hint 的不同做不同的行为, 这里的 hint 是 'number', 定义 methodNames 为 ["valueOf", "toString"]

Note: 如果 hint 是 'string' 的话, 那么 methodNames 为 ["toString", "valueOf"]

按照 methodNames 的顺序进行调用:

  1. [].valueOf() 注意, Array.prototype 没有实现 valueOf 方法, 是继承自 Object.prototype 的, 所以我们还需要看一下 Object.prototype.valueOf 返回了什么

    你可以看到其返回了 ToObject(this value), 这里 this 指向的就是 [], 根据 ToObject 的算法, 如果是 Object 就返回本身, 所以调用 [].valueOf() 返回的是 [] 本身. 回到 OrdinaryToPrimitive 算法中, 如果 valueOf 返回的是一个 Object, 那么就继续调用 toString

  2. [].toString(), 注意, Array.prototype 实现了 toString 的方法, 所以这里调用的是 Array.prototype.toString, 看一下 Array.prototype.toString 做了什么,

    其实就是调用了 Array.prototype.join, join 方法我不再赘述, 空数据会返回一个空字符串 '', OK, 回到 OrdinaryToPrimitive 算法中, '' 空字符串不再是 Object, 将其返回

回退

OrdinaryToPrimitive 返回了 空字符串 '' 给 ToPrimitive

ToPrimitive 返回了空字符串 '' 给 ToNumber

继续 ToNumber

再次调用 ToNumber, 将 '' 空字符串传入

进入 字符串转 Number 的算法

在这里有这么一句话, 如果一个 StringNumericalLiteral 是空的或者是只有 white space, 其会转换成 +0

ToNumber 返回 0

ToNumber 算法结束, 返回 0

Unary + Operator 返回 0

这个 0 将打印在你的终端或者是浏览器的控制台上.

结语

ECMAScript 对于算法的解释详细程度令人吃惊, 这也是我第一次这么去读规范, 其实不是很费脑子.

基本和 @ygming 在沸点下的评论一致, juejin.cn/pin/6850047…

Thanks ALL! ✅