JavaScript 语法中 {} 存在二义性:语句开头被解析为语句块,否则为对象直接量。本文通过 {} + [] 等示例,解析编译器处理逻辑及标签语句机制,揭示常见语法陷阱。
JavaScript 中花括号 {} 的二义性与解析陷阱
JavaScript 语法的复杂性广为人知。
首先通过一张示意图进行说明。

具体代码示例如下:
代码片段展示:
{} + []; // 返回 0
[] + {}; // 返回 "[object Object]"
{} + [] == [] + {}; // 结果为 false
({} + [] == [] + {}); // 结果为 true
这种令人头疼的语法特性,在 JavaScript 中确实较为罕见。
对于大多数不深入研究 JavaScript 编译原理的开发者而言,这很难理解(至少对我来说也是如此)。
经过查阅相关资料后,才逐渐理清了思路。
接下来,我们先分析这段代码:
代码展示:
{
a: 1
}
大多数开发者第一眼可能会将其误认为是一个对象直接量。
那么这段代码呢?
代码展示:
{
var a = 1;
}
浏览器会提示语法错误吗?
显然不会。仔细分析可知,这实际上是一个语句块。
代码展示:
if (isNumber) {
var a = 1;
}花括号二义性与编译器处理逻辑
由此可以看出,JavaScript 中以 { 开头的代码存在二义性。
JavaScript 编译器如何处理这种二义性?
为了解决这一问题,ECMA 采取了简单直接的方法:在语法解析阶段,如果一个语句以「{」开头,则将其解释为语句块。
这确实是一种令人诟病的处理方式。
既然都是语句块,为什么 {a:1} 却没有语法错误?
在此情况下,a 被解析器识别为标签。标签用于配合 break 和 continue 语句进行定向跳转。
因此,以下写法会抛出异常:
代码展示:
{
a: function () {}
}
因为 function () {} 既不是函数声明,也不是函数表达式。
具体代码示例解析
至此,大家对 {} 的特殊处理应有基本了解。我们回到文章开头提到的几条语句:
代码展示:
{} + []; // 返回 0
[] + {}; // 返回 "[object Object]"
{} + [] == [] + {}; // 结果为 false
({} + [] == [] + {}); // 结果为 true
第一条中,由于 {} 是语句块,代码可理解为:
代码展示:
if (1) {}
+[]
因此返回值为 0。
第二条中,{} 不在语句开头,故视为正常的对象直接量。空数组与空对象直接相加,返回 "[object Object]"。
理解了前两条,第三条无需多言。
第四条中,因以 () 开始,第一个 {} 被解析为对象直接量,故两条公式相等,返回 true。