【问题标题】:Differentiate a block from an object initializer将块与对象初始化器区分开来
【发布时间】:2014-02-03 08:11:09
【问题描述】:

这是一个理论问题,而不是实际问题。这是关于解析一些由花括号分隔的代码。

这里有两个object initializers 的例子:

f({});
({a:3})

以下是blocks 的两个示例:

;{}
{a:3;}

在实践中,如果前面的代码需要表达式,{...} 似乎会分开一个块。

但是我从来没有在 ECMAScript 规范中看到过这样的明确或明显的规则,我什至不确定这是真的。

在某处是否有明确的、明确的参考?如果这不是正确的规则?

【问题讨论】:

  • ExpressionStatement: [lookahead ∉ {{, function}] Expression;
  • @Bergi 对于刚从假期回来,咖啡还没有开始的可怜的程序员,你能说得更清楚些吗?
  • 表达式语句不能以大括号开头,因此“由大括号分隔的语句”始终是一个块。所有其他花括号都不是语句,而是表达式 - 对象初始化器。
  • @dystroy ({}) 是一个表达式。这就是为什么你在处理 eval 时用 () / 包装 json
  • (仅相关信息) eval 用于 JavaScript 表达式、语句或语句序列..但有时我们需要将其解析为表达式。例如当我们得到像 '{"employees":"John"}' 这样的 json 时,我们需要确保它不会被用作语句(这会导致错误,因为它认为(作为语句)它是一个块代码但语句不能与对象文字“员工”一起使用。所以我们用 (..) 包装它,现在它将被解析为对象而不是语句。(这将导致错误)。示例:@987654328 @ //错误。而eval('({"a":"1"})') 不是

标签: javascript syntax grammar ecmascript-5


【解决方案1】:

在某处是否有明确的、明确的参考?

它有点遍布规范。

简短的回答:

这取决于构造出现的上下文(这就是为什么它在规范中到处都是)。它所涉及的最具体的地方可能是§12.4,它表示ExpressionStatement(用于预期语句的表达式)不能以{ 开头。

长答案:

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

doThis();   // This line is just for context
x = {a: 3};

在上面第二行的开头,解析器期待一个语句。但随后它看到x = 并知道它正在处理分配;此时,在看到= 之后,解析器期待一个表达式statement 在那里无效。所以它知道{ 启动了一个对象初始化器,而不是一个块。

相比之下:

doThis();   // This line is just for context
{a: 3};

上面的第二行是一个包含标签语句的块。 (看起来很奇怪;我们会回到那个。)解析器知道,因为在该行的开头,解析器期待的是一个语句,而不是一个表达式。

解析器希望在许多其他地方看到表达式,而不是语句。例如,在属性初始化器中的 : 之后:

obj = {
    prop: {a: 3}
};

...或在执行函数调用时的参数中:

foo({a: 3});

...或在一元运算符之后,或在打开( 等之后。在规范中,您可以通过语法中所说的解析器正在解析的内容来判断解析器的期望,例如此语法图来自 §12.5 定义 if 语句:

 IfStatement :

if ( Expression ) 语句 else 语句
if ( 表达式 ) 语句

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

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

doThis();   // This line is just for context
flag && doThat();

上面的第二行是一个二元逻辑运算符表达式,但是是独立的。解析器在遇到它时期待一个语句。所以&& 表达式就是规范中所说的ExpressionStatementExpressionStatement§12.4 定义。

这给我们留下了一些歧义:如果解析器期待一个语句,并看到一个 {,它怎么知道这不是作为 ExpressionStatement 的对象初始化表达式的开始 ?

答案是:通过法定货币。 :-) §12.4 在定义 ExpressionStatement 时这么说:

注意 ExpressionStatement 不能以左大括号开头,因为这可能会使它与 Block 混淆。

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

(如果您有理由非常非常想使用对象初始化表达式作为语句,您可以这样做;只需将其放在 () 中即可。)

【讨论】:

  • @MaxArt:JavaScript 引擎不会“尝试忽略分号”。
  • @T.J.Crowder - “期望右手值”是什么意思?浏览器必须知道a: 是否为标签,以便期望或不期望右手值。
  • 什么是“右手值”?规范没有使用这样的术语;对象字面量是“主要表达式”,也可以出现在/作为左侧表达式。
  • @T.J.Crowder:啊,我明白了你的定义。但是,这是错误的——对象初始化器不仅可以作为赋值的右侧或作为函数参数出现;它们是有效的LeftHandSideExpressions。示例:false && ({a:5}=3) 完全有效。
  • @T.J.Crowder, Derek:我的意思是它的语法有效(不抛出SyntaxError),并不是说它在执行时有意义:-)
猜你喜欢
  • 2021-11-10
  • 2015-01-10
  • 2013-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多