区分块与对象初始值设定项

Den*_*ret 6 javascript syntax grammar ecmascript-5

这更像是一个理论问题,而不是一个实际问题.它是关于解决由花括号分隔的一些代码.

以下是对象初始值设定项的两个示例:

f({});
({a:3})
Run Code Online (Sandbox Code Playgroud)

以下是两个块的示例:

;{}
{a:3;}
Run Code Online (Sandbox Code Playgroud)

实际上,{...}如果先前的代码需要表达式,那么它似乎会分开一个块.

但我从来没有在ECMAScript规范中看到这样的规则明确或明显,我甚至不确定它是否属实.

某处有明确的非模棱两可的参考吗?一个正确的规则,如果这个不是?

T.J*_*der 7

某处有明确的非模棱两可的参考吗?

它有点遍布整个规范.

简短的回答:

它取决于构造出现的上下文(这就是为什么它完全超出规范).可能它所解决的最具体的地方是§12.4,它表示ExpressionStatement(在预期语句时使用的表达式)不能以a开头{.

答案很长:

关键是解析器遇到{它时所期望的:如果它期望一个语句,那么它知道{开始一个块.但是如果它期望一个表达式,那么它知道{开始一个对象初始化器.我们看一下作业:

doThis();   // This line is just for context
x = {a: 3};
Run Code Online (Sandbox Code Playgroud)

在上面第二行的开头,解析器期待一个语句.但后来它看到x =并且知道它正在处理任务; 在那一点上,在看到之后=,解析器期待一个表达式.一个声明是无效的存在.所以它知道{启动对象初始化器,而不是块.

相反:

doThis();   // This line is just for context
{a: 3};
Run Code Online (Sandbox Code Playgroud)

上面的第二行是一个包含带标签的语句的块.(一个非常奇怪的一个;我们将回到那个.)解析器知道,因为在该行的开头,解析器期望一个语句,而不是一个表达式.

解析器期望看到表达式而不是语句的许多其他地方.例如,在:属性初始值设定项之后:

obj = {
    prop: {a: 3}
};
Run Code Online (Sandbox Code Playgroud)

...或在进行函数调用时在参数内:

foo({a: 3});
Run Code Online (Sandbox Code Playgroud)

...或者在一元运算符之后,或者在开放之后(,等等.在规范中,您可以通过语法中解释器解析的内容来说明解析器将会期望什么,例如来自§的语法图12.5定义if声明:

 IfStatement :

if ( Expression ) Statement else Statement
if ( Expression ) Statement

这告诉我们,在处理if语句时,()解析器中需要一个表达式,但在该if ()位之后,它需要一个语句.

到目前为止一直很好,但JavaScript允许(几乎)任何表达式允许的语句.这是有效的,例如:

doThis();   // This line is just for context
flag && doThat();
Run Code Online (Sandbox Code Playgroud)

上面的第二行是二元逻辑运算符表达式,但是是独立的.解析器在遇到它时期待一个语句.因此&&表达式是规范称为ExpressionStatement的表达式.ExpressionStatement§12.4定义.

这让我们有些含糊不清:如果解析器期望一个语句,并且看到一个{,那么它如何知道这不是作为ExpressionStatement的对象初始化表达式的开头?

答案是:通过命令.:-)§12.4在定义ExpressionStatement时这样说:

注意ExpressionStatement不能以开括号大括号开头,因为这可能使其与Block不一致.

所以没有歧义,不是因为一些微妙的语法技巧,而是因为规范说明了这一点.:-)

(如果你有理由真的,真的想要使用对象初始化表达式作为语句,你可以这样做;只需将它放在其中().)

  • @MaxArt:JavaScript引擎不"试图忽略分号". (4认同)