所以,你可能知道,JavaScript的严格模式增加了对标识符的限制eval
,arguments
,有效地使它们成为保留字,但有两个例外:
- 它们仍然可以用作表达式,即在需要表达式的地方(不过有几个例外),
- 它们仍然可以用作标签名称,并在
break
/continue
语句中引用标签。
现在,我明白了第一颗子弹。例如,如果不允许将标识符eval
作为表达式(而真正的保留字则不允许),我们将根本无法调用eval
函数(因为eval(str)
是一个表达式)。arguments
也是如此 - 我们需要能够将其用作表达式才能访问其元素(例如arguments[0]
)。
不过,此规则有三个例外。eval
/arguments
可能不会在作业中显示为左侧表达式(例如eval = true;
), (2) 作为++
/--
的操作数(例如eval++
), (3) 作为delete
的操作数(例如delete eval
)。不过,这些是唯一的例外,在所有其他表达式上下文中,它们都是有效的。
我不明白的是第二颗子弹。为什么它们仍然可以用作标签名称?例如,即使在严格模式下,此代码也有效:
eval: for ( var i = 0; i < 10; i++ ) {
arguments: for ( var j = 0; j < 10; j++ ) {
if ( i < j ) continue eval;
console.log( i - j );
}
}
请注意continue eval;
如何引用eval
标签,与实际的eval
函数无关。
另请注意,真正的保留字不能用作标签名称。根据我的理解,严格模式的意图是使名称eval
,并且arguments
尽可能像保留词一样。那为什么要将它们保留为有效的标签名称呢?
我不知道为什么,但我可以冒险猜测一个很好的猜测。
eval
和arguments
仅限于变量名的原因是(就此而言,为什么禁止with
)是因为它们会影响变量名的绑定位置(即到局部变量、封闭范围内的变量或全局对象上的属性(如果存在)。 如果没有这些限制,某些名称在运行时之前是不受约束的,这意味着所有危险。
标签名称占用与变量名称、属性名称等不同的命名空间。break
/continue
的目标永远不会有歧义,因为标签是静态分配的,标签引用是静态解析的。 (是的,eval
代码可以包含标签。 但是现有的跳转不能以这些标签为目标,eval
代码中的跳转也不能以预先存在的标签为目标,因为标签解析发生在编译时,并且它的范围限定为程序,以使用 ECMAScript 终端的名称。 包含eval
调用的脚本是程序,eval
调用执行的代码是程序,但出于标签定位目的,两者是完全分开的。
禁止变量名称eval
和arguments
的原因根本不适用于标签。 因此,标签仍可命名为eval
或arguments
。 以这种方式命名标签是愚蠢的,真的。 如果有人再次设计 ECMAScript/JavaScript,它们将是关键字,不能用作标签名称。 但是禁止它们作为标签名称没有任何好处,而且至少有一个不禁止它们的兼容性论据,所以它们没有被禁止。