你可能看过Gary Bernhardt的 WAT的演讲,他在其中谈到了这些令人困惑的JavaScript代码:
{} + [] // 0
[] + {} // "[object Object]" 【译者注:Object.prototype.toString.call({})的结果是"[object Object]"】
[] + [] // "" 【译者注】
{} + {} // "[object Object][object Object]" 【译者注】
他向观众展示了一个对象和一个数组相加结果是0,但是一个数组和一个对象相加结果是"[object Object]"。瞧,如果您在chrome控制台中尝试此操作,那么您将获得:
但是,如果将这些行用括号括起来,则可以看到这两行的计算结果相同:
发生什么了?让我们看一下抽象语法树,看看语法上是否有奇怪的事情发生。
如果我们将{} + []插入ASTExplorer,得到这个:
一个空对象后面跟着一个一元+操作符?那不是对象加数组!那是……完全不同的东西。
为了证实我们的怀疑,我们看到Javascript的作用于数组的一元+运算符的值为0,表明我们的解释确实正确。空代码块被评估为noop,剩下的就是一元表达式:
我们再去ASTExplorer 以确保[] + {}实际上像我们期望的那样向对象添加了一个数组:
是的!我们的理解在这里看起来不错!因此,如果我们对语法感到困惑,请将其插入ASTExplorer,然后一切都会显示出来,对吗?
但是故事还没结束。还记得我们如何将某些内容粘贴到ASTExplorer中以查看其解析,然后将其粘贴到Chrome控制台中以查看其行为吗?让我们使用常规的JavaScript对象尝试相同的过程。 Chrome会执行我们期望的操作并创建一个对象:
但是,当将其粘贴到ASTExplorer中时,将得到以下内容:
什么?为什么ASTExplorer认为这是语法错误?我们正在声明一个对象!这就像JavaScript的第一天! ASTExplorer怎能不赶上呢?
事实证明ASTExplorer实际上就在这里,而Chrome正是其中一种怪异的表现。任何其他语句之外的裸对象都不会被解析为裸对象,而是被解析为一个代码块,就像之前一样!由于代码块的内部内容不作为语句进行解析,因此解析失败,从而导致冒号上的语法错误。
通过eval
可以确信这是对的:
当Chrome控制台出现明显的语法错误时,为什么最终会创建对象? Chrome之所以这样做,是因为当您将某些内容粘贴到Chrome控制台中时,您本能地拥有两种不同的行为期望:
- Chrome控制台会计算语句。
- 将表达式粘贴到Chrome控制台中应计算该表达式。
这两个期望从根本上彼此冲突,并且无法调和。它们导致了如何解析裸对象的根本矛盾。 为了弥补这一差距,Chrome浏览器会动态找出是将行解析为“语句”还是“表达式”。作为表达式,该行最终求值到一个对象,但是作为语句,该行最终出现语法错误。 Chrome改变了他们在此处使用的确切规则,但是他们遵循的规则似乎是这种行为:
- 查看输入是否以{开头并以}结尾
- 如果不是,则将输入解析为一系列语句(statement)
- 如果是,则将输入解析为表达式(expression)
- 如果失败,则回过头来将输入解析为一系列语句
我大约有90%的把握就是今天的样子,但是我也没有像一年前那样检查,而且我也不会深入研究Chrome源码来再次解决问题。
总结下:
- 对象加数组不是0,及时在Chrome控制台看起来是0
- 如果对语法感到疑惑,使用ASTExplorer
- Chrome控制台是计算语句和表达式的好方法,但是...
- Chrome控制台有一些狭窄的陷阱
- 如果你还是感到困惑,
eval
可以告诉你真相的来源
译者总结的相关"八股文"及其相关理解:
加法会进行隐式转换,规则是调用其 valueOf() 或 toString() 以取得一个原始值。如果两个值中的任何一个是字符串,则进行字符串拼接,否则进行数字加法。
[] 和 {} 的 valueOf() 都返回对象自身,所以都会调用 toString()。
[].toString() 返回空字符串,({}).toString() 返回“[object Object]”。
+[] // 0 【对一个空数组执行正号运算,实际上就是把数组转型为数字。先调用 [].valueOf()返回自身,继续调用[].toString(),返回空字符串。空字符串转型为数字0】
+{} // NaN
[].toString() // ""
{}.toString() // Uncaught SyntaxError: Unexpected token '.'
({}).toString() // "[object Object]"
[] + {} // "[object Object]" 【隐式转换为: "" + "[object Object]"】
{} + [] // 0 【{}被解析为empty block了。这里本质上是对一个空数组执行正号运算,实际上就是把数组转型为数字。】
[] + [] // "" 【隐式转换为两个空字符串相加】
{} + {} // "[object Object][object Object]"
({} + []) // "[object Object]" 【{}被解析为空对象,本质上和上面的 []+{} 类似】
参考:
-
原文Object Plus Array Is Not Zero
-
Javascript 中 {}+[] === 0 为true因为啥
-
JS中{}+[]和[]+{}的返回值情况是怎样的?
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!