最近在代码中,不小心在switch中定义了一个块级变量,导致页面在某些浏览器中无法访问。本文讨论switch语句级别范围内的以下块。Block-levelscopeinswitchstatementswitchstatementblock-levelscope可能存在的问题规范和检测本文原文在我的博客,https://github.com/forthealll...欢迎star1.switch语句Block-levelscopein‖ES6或TS引入了block-levelscope。块级作用域内的变量可以通过let、const、class等定义,块级作用域内的变量没有变量提升,存在暂时的死区。常见的if语句和for循环可以在循环体中定义块级变量。那么什么是switch语句中的块级作用域呢?先给出结论:switch语句中的块级作用域,在整个switch语句中,而不是为每个case生成一个独立的块级作用域。下面举几个例子来说明这个问题:letnumber=1;switch(number){case1:letname='Jony';default:console.log(name)}上面的代码会输出jony。再看一个例子:letnumber=1;switch(number){case1:letname='Jony';休息;case2:letname='yu';休息;default:console.log(name);}像这样一生都会重复的错误:UncaughtSyntaxError:Identifier'name'hasalreadybeendeclared上面两个例子说明在switch语句中,整个switch语句构成了一个块级范围。不管大小写,每个case都不构成一个独立的块级作用域。2.switch语句中块级作用域可能出现的问题我们知道switch语句,整个switch语句的最顶层是一个块级作用域,但是也要注意case的特殊性。在case中声明的变量不会被提升到块作用域中。letnumber=2;switch(number){case2:name='yu';break;}在这个例子中,虽然没有声明name,但是给name赋值相当于复制了全局的window对象,即window.name='yu'。不会有问题的。这里出现了一个有趣的问题:letnumber=2;switch(number){case1:letname='jony';休息;case2:name='yu';break;}本例会报错,name会报undefinederror。如果是UncaughtReferenceError:nameisnotdefined的原因,虽然在case中定义的block级不会有变量提升,但是会存在一个暂时的死区,即如果没有执行letname='jony',即namedefinition过程没有执行,那么name在整个块级作用域内都不可用,是undefined。为了证明我们的想法,再改写上面的例子:letnumber=1;switch(number){case1:letname='jony';休息;case2:name='yu';break;}我们把number改成1,发现代码不会报错,因为此时已经执行了letname的定义和赋值。三、规范和检测1、什么时候会出问题??可以说为什么在自己的项目中,即使在ES6或TS代码中有上述错误使用,也不报错?笔者之前也遇到过这个问题。需要明确的是,你是直接把ES6或者TS代码转成es5,然后在线调试或者发布。当let编译成es5的时候,上面的switch当然就不存在了。范围问题。但在现实中,编译成es5的js文件可能太大了。对于高版本浏览器,我们希望直接使用ES6代码(通过type=module来判断浏览器对ES6的支持),那么就会出现上面的问题。2.Howtodetectandavoid‖‖如何避免这种情况?当然,最好的办法不是在case中定义块级变量,而是如果不小心写了上面的问题代码怎么检测。首先,使用typescript,静态编译不能导致错误提示,因为这个错误是运行时异常。最好的办法就是通过编写eslint规范来解决上述非法使用问题。
