ECMAScript 标准的十万个为什么 - Symbol 的隐式转换 - CodeSky 代码之空

586 阅读2分钟
原文链接: codesky.me

今天苏老师发了一篇:Symbol Polyfill 填坑之旅,文章中提到 Symbol 隐式转换的问题,作为一个只在文章中看过 Symbol 的萌豚,我也想知道究竟为什么 Symbol 可以有 toString() 方法,却不能进行 '' + Symbol('word') 的操作。

标准

MDN - Symbol 中有一些规则,它告诉我们当 Symbol 尝试隐式类型转换时会报错,但是为什么会这样,底层是是怎么实现的,我们还是不太清楚。

Symbol 类型转换

当使用 symbol 值进行类型转换时需要注意一些事情:

尝试将一个 symbol 值转换为一个 number 值时,会抛出一个 TypeError 错误 (e.g. +sym or sym | 0).

使用宽松相等时, Object(sym) == sym returns true. 这会阻止你从一个 symbol 值隐式地创建一个新的 string 类型的属性名。例如,Symbol("foo") + "bar" 将抛出一个 TypeError (can't convert symbol to string).

"safer" String(sym) conversion works like a call to Symbol.prototype.toString() with symbols,但是注意 new String(sym) 将抛出异常。

标准实现

我们从底层标准看起,首先看 + 时发生了什么:

Let lref be the result of evaluating AdditiveExpression.
Let lval be ? GetValue(lref).
Let rref be the result of evaluating MultiplicativeExpression.
Let rval be ? GetValue(rref).
Let lprim be ? ToPrimitive(lval).
Let rprim be ? ToPrimitive(rval).
If Type(lprim) is String or Type(rprim) is String, then
Let lstr be ? ToString(lprim).
Let rstr be ? ToString(rprim).
Return the String that is the result of concatenating lstr and rstr.
Let lnum be ? ToNumber(lprim).
Let rnum be ? ToNumber(rprim).

会在左值和右值赋值之后依次执行 GetValueToPrimitive,对于 String 和 Symbol 而言,会返回原来的值。接下来,假设操作为 '' + Symbol('word'),左值为 String ,执行 ToString 操作,此处的 ToString 并不是我们所说的原型链上的 toString 方法,我们需要接着往下看:

14969339026287.jpg

换句话说,在 ToString 方法内部判断了值类型,根据类型进行后续不同的操作,而不是简单的调用 toString() 方法,对于 Symbol 类型,它的处理就是抛出异常。

如果我们需要 polyfill,需要进行一系列重载或者复写底层定义,不太好搞。

相关文档