Javascript ES2015中的switch语句和作用域
考虑此ES2015模块以及在节点v4.4.5中运行时的行为Javascript ES2015中的switch语句和作用域,javascript,node.js,ecmascript-6,v8,Javascript,Node.js,Ecmascript 6,V8,考虑此ES2015模块以及在节点v4.4.5中运行时的行为 'use strict' const outer = 1 switch ('foo') { case 'bar': const heyBar = 'HEY_BAR' break case 'baz': const heyBaz = 'HEY_BAZ' break default: const heyDefault = 'HEY_DEFAULT' } console.log( o
'use strict'
const outer = 1
switch ('foo') {
case 'bar':
const heyBar = 'HEY_BAR'
break
case 'baz':
const heyBaz = 'HEY_BAZ'
break
default:
const heyDefault = 'HEY_DEFAULT'
}
console.log(
outer, // 1, makes sense, same top-level scope
heyBar, // undefined. huh? I thought switch did NOT create a child scope
heyBaz, // undefined. huh? I thought switch did NOT create a child scope
heyDefault) // 'HEY_DEFAULT' makes sense
这在我看来是内在矛盾的。如果switch语句没有创建词法作用域,我希望所有hey*
变量都是主作用域的一部分,并且行为一致。如果switch语句确实创建了一个词法范围,我仍然希望它们是一致的,但是case
子句中声明的变量的行为就像它们在子范围中一样,而default
子句中的变量的行为就像它在外部范围中一样
我的问题是switch语句中是否涉及子作用域,如果是,它们的行为细节是什么
在NodeV6.4.0中,行为是不同的。看起来开关块确实创建了一个子块作用域
ReferenceError: heyBar is not defined
这似乎更容易理解。我根本无法重现你的行为。我立即得到一个
参考错误(节点6.4.0和当前的Firefox):
这对我来说似乎是正确的行为。带括号的AFAIKswitch
语句确实创建了块,因此为块范围的实体创建了词法范围。case
语句本身并不创建自己的块
如果我们在switch
语句中用foo
大小写来扩展这个示例,它也会抛出ReferenceError
:
'use strict'
const outer = 1
switch ('foo') {
case 'bar':
const heyBar = 'HEY_BAR'
break
case 'baz':
const heyBaz = 'HEY_BAZ'
break
case 'foo':
const heyFoo = 'HEY_FOO'
break
default:
const heyDefault = 'HEY_DEFAULT'
}
console.log(
outer,
heyFoo,
heyBar,
heyBaz,
heyDefault) // ReferenceError: heyFoo is not defined
参考
其中是开关盒的block语句
这大致可以理解为:
在switch语句的块中创建一个新的块环境(switch{}
),并实例化所有块级声明(例如let
、const
或块级函数声明)
switch
为switch(…){…}
语句创建范围,但不创建case
语句的范围。请参阅。开关的主体创建一个新的块作用域。每个单独的case
子句或default
子句不会自动创建新的块范围
ReferenceError: heyBar is not defined
当然,理解作用域和开关语句的最终参考是。然而,要弄清楚它到底在说什么还需要一些工作。我将尝试引导您了解我可以从该规范中遵循的内容
定义重要术语
首先,switch语句定义如下:
SwitchStatement:
switch ( Expression ) CaseBlock
一个案例是:
CaseBlock:
{ CaseClauses }
{ CaseClauses DefaultClause CaseClauses }
CaseClauses:
CaseClause
CaseClauses CaseClause
CaseClause:
case Expression : StatementList
DefaultClause:
default : StatementList
因此,CaseBlock
是switch
语句的主体(包含所有案例的代码)
这是我们所期望的,但是上面定义的术语CaseBlock
对于其他规范参考非常重要
案例块创建新范围
然后,在中,我们可以看到CaseBlock导致创建一个新的范围
当对块或CaseBlock生产进行求值时,将生成一个新的声明性语句
创建环境记录,并对每个块范围进行绑定
中声明的变量、常量、函数、生成器函数或类
块在环境记录中实例化
由于CaseBlock
是switch
语句的主体,这意味着switch
语句的主体将创建一个新的块范围(用于新let/const声明的容器)
CASE子句添加到现有范围(不创建自己的范围)
然后,在中,它描述了解释器在解析时如何收集词汇范围的声明。以下是规范中的实际文本(解释如下):
CaseBlock:{CASECLASSIONS DEFAULTCLASSION CASECLASSION}
如果存在第一个case子句,则将声明设为第一个case子句的词汇范围声明
否则,让声明成为一个新的空列表
将Default子句的LexicalyScopedDeclarations元素附加到声明中
如果第二个case子句不存在,则返回声明
Else返回将第二个case子句的lexicalyScopedDeclarations元素附加到声明的结果
案例条款:案例条款
让声明成为case子句的词汇范围声明
将LexicalyScopedDeclarations of Case子句的元素附加到声明中
返回声明
CaseClause:case表达式:语句列表
如果存在StatementList,则返回StatementList的LexicalyScopedDeclarations
否则返回一个新的空列表
DefaultClause:default:StatementList
如果存在StatementList,则返回StatementList的LexicalyScopedDeclarations
否则返回一个新的空列表
所以,基本上这是说第一个case子句创建了一个lexicalyScopedDeclarations对象。然后,后面的每个DefaultClause或CaseClause都会附加到该声明对象。这就是规范描述在一个范围内构建所有声明的方式
CaseClaquence
附加到现有的declarations对象后,它不会创建自己的声明。这意味着它不创建自己的范围,而是使用包含范围
当然,您可以在CaseClause
中定义一个块,然后该块将成为它自己的作用域,但是CaseClause
不需要块声明,因此默认情况下它不会创建新的作用域
运行时语义:评估
然后,那里
CaseBlock:
{ CaseClauses }
{ CaseClauses DefaultClause CaseClauses }
CaseClauses:
CaseClause
CaseClauses CaseClause
CaseClause:
case Expression : StatementList
DefaultClause:
default : StatementList
const var1 = true;
switch (var1) {
case true: {
const var2 = 0;
break;
}
case false: {
const var2 = 1;
break;
}
default: {
const var2 = 2;
}
}