我查看了规范,不明白为什么这段代码会导致错误:
L: function a(){
console.log(1);
break L; /// Uncaught SyntaxError: Undefined label 'L'
console.log(2);
}
a();
在使用代码块的同时,它运行良好:
M: {
console.log(1);
break M;
console.log(2)
}
顺便说一句,我可以逐个标记函数,而不用在函数内部使用那个标签。我不会得到错误:
L: function a(){
console.log(1);
console.log(2);
}
a();
主要问题:如果我们不能在函数内部使用标签,那么标签函数语句的主要目的是什么?
TLDR
到20世纪90年代中期,CCD_;危险的";令人困惑,因此被排除在新语言之外。但突然退出或";跳跃;块外构造(块、try、with、if、loop构造)仍然是一个理想的特性,因此标签和break
/continue
被包括在内,作为一种淡化的替换。
请注意,标签和break
/continue
无法启用更强大的goto
的循环行为:它们只允许您有效地向前跳出块。
函数也是类似块的构造。根据规范,函数不是语句,因此不能标记,但在网络的早期,标准被视为仅仅是灵活的指导方针;因此,浏览器供应商不一致地启用了函数的标记("标记的函数语句")[这将为return
提供一种替代语法?]这种行为不在原始标准中,但在ES2015中进行了向后标准化,以便在break <label>
/continue <label>
引用了标记的函数声明时抛出错误,尽管出于向后兼容性的原因,不引用标签的带标签的函数语句将被允许。
这解释了你观察到的行为。
详细信息
根据规范,您可以用JavaScript标记所有语句。自从ECMAScript 2.0版本以来,这种情况一直存在,当时标签被添加到该语言中。
以下是JS中的语句类型:
BlockStatement,VariableStatement,EmptyStatement,ExpressionStatement、IfStatement、BreakableStatement、,ContinueStatement,BreakStatement,ReturnStatement,WithStatement,Labeled语句、ThrowStatement、TryStatement、Debugger语句。
请注意,块是语句。
但是标签只能用于goto
0和continue
,因此在允许的情况下标记其中一些语句(例如,Variable、Return、Throw)是毫无意义的。
这意味着标签可以应用于不能使用break
和continue
的结构,使这些位置的标签实际上只能用作伪注释。
该语言是这样设计的,因为它通过避免对";死标签分析";。
"函数语句";是函数";在语句位置";。ECMAScript 5.1有以下内容:
ECMAScript的几个广泛使用的实现已知支持将FunctionDeclaration用作语句。然而,在应用于此类FunctionDeclarations的语义中,实现之间存在显著且不可调和的差异。由于这些不可调和的差异,将FunctionDeclaration用作语句会导致代码在实现之间无法可靠地移植。建议ECMAScript实现不允许使用FunctionDeclaration,或者在遇到这种使用时发出警告。ECMAScript的未来版本可能会定义在Statement上下文中声明函数的可移植方法。
这意味着在一些较旧的浏览器中;声明";有时可以被视为一种陈述,因此它们可以被贴上标签。我不知道历史浏览器的标签功能的确切行为是什么。如果您试图引用标签,现代浏览器会引发语法错误,正如您所发现的那样。
因此以下(try
语句)是有效的JS:
let foo = 'a'
myLabel: try {
if(foo === 'a') break myLabel
console.log('this will not be printed')
} catch {}
console.log('all done')
并且以下(switch
语句)有效:
let foo = 'a'
myLabel: switch(foo) {
case 'a':
break myLabel
console.log('this will not be printed')
}
console.log('all done.')
但是,以下(return
语句)也是有效的(但毫无意义):
function foo() {
myLabel: return 'result of foo'
}
console.log(foo())
这个变量语句的标签也毫无意义:
myLabel: var foo = 'value of foo'
console.log('works fine, but the label was pointless')