我刚刚浏览了JSLint的源代码,并注意到了这段代码:
// Is this a labeled statement?
//...
if (next_token.labeled !== true || funct === global_funct) {
stop('unexpected_label_a', label);
} //...
有趣的部分是
funct === global_funct
比较。通过 JSLint 运行以下代码片段会引发“意外标签”错误,因为带标签的语句位于全局执行上下文中(我知道,这是一个愚蠢的示例。这是一个小提琴。):
loop:
for (var i = 0; i < 10; i++) {
if (i === 5) {
break loop;
}
}
如果您将相同的代码片段放入函数中,JSLint 对此非常满意,并且在遇到标签时不会抛出错误。 这是一个小提琴,其中包含将通过 JSLint 的代码。如果您想尝试的话,可以将代码粘贴到在线版 JSLint 中。
所以我的问题:在全局代码中使用带标签的语句有什么问题吗?还是这只是 Crockford 的另一个个人选择?
经过对标签语句行为的一些调查,我认为这实际上只是 Crockford 的选择,没有任何事实依据。据我所知,没有任何情况会导致与全局范围内的标签发生命名冲突(这似乎是人们想到的 JSLint 不允许这样做的主要原因 - 请参阅对该问题的评论)。
ES5 规范在有关标记语句的部分中规定了以下内容:
生产标识符:声明通过添加来评估 Identifier 到Statement的标签集,然后评估Statement。
...
在评估LabelledStatement之前,所包含的Statement被视为拥有一个空标签集,除非它是一个IterationStatement或SwitchStatement,在这种情况下,它被视为拥有一个由以下组成的标签集:单个元素,
。empty
我认为这意味着每个语句都有一个标签集。标签标识符独立于变量和函数标识符,因此在语法上可以接受与同一范围内的变量具有相同标识符的标签。换句话说,这是有效的:
var label = "My Label";
label:
for (var x = 1; x < 10; x++) {
break label;
}
由于每个语句都有自己的标签集,因此这也是有效的:
label:
for (var x = 1; x < 10; x++) {
//Looks for 'label' in label set of `break` statement, then `for` statement
break label;
}
label:
for (var y = 5; y < 15; y++) {
//Same again. Will never look for label outside the enclosing `for` statement
break label;
}
既然你可以标记任何语句(这是没有意义的,但这是可能的),你可以标记一个带标签的语句:
another:
label:
for (var y = 5; y < 15; y++) {
break label;
}
在这种情况下,规范规定如下:
如果LabelledStatement本身有一个非空的标签集,这些标签也会被添加到之前的Statement的标签集中 评价一下。
在上面的代码片段中,
for
语句的标签集包含两个标签(another
和label
)。可以从 for
语句中中断到这些标签中的 任一。
最后,规范还指出(强调):
带标签的语句仅与带标签的
结合使用 和break
陈述。 ECMAScript 没有continue
声明。goto
基于此,我想不出全局代码中的任何标签可能干扰其他全局代码的方法。当然,您不太可能想要一个包含具有相同标识符的多个标签的程序,并且 JSLint 已经通过抛出“标签已定义”错误来防止这种情况发生。但我认为它在全局执行上下文中处理标记语句的方式应该没有任何区别。