首先,一个简单的问题。下面哪段代码会报错:先创建实例,再定义使用的类:newCar('red');//会报错吗?classCar{constructor(color){this.color=color;}}orfirstCallthefunctionbeforethefunctiondefinition:greet('World');//会报错吗?functiongreet(who){return`Hello,${who}!`;}正确答案是:第一个代码片段会报ReferenceError:Cannotaccess'Car'beforeinitialization错误。第二个代码工作正常。如果您的答案与上述不同,或者您只是猜测而不知道背后的原理是什么,那么您需要了解临时死区(TDZ)。TDZ管理let、const和class语法的可用性。变量在JS中的工作方式非常重要。1.什么是临时死区让我们从一个简单的const变量声明开始。首先声明并初始化变量,然后访问它,一切正常:constwhite='#FFFFFF';white;//=>'#FFFFFF'如果在声明之前访问white变量会发生什么情况?white;//抛出`ReferenceError`constwhite='#FFFFFF';white;在constwhite='#FFFFFF'语句之前的代码行中,变量white处于临时死区。在TDZ访问white后,JS抛出ReferenceError:Cannotaccess'white'beforeinitialization临时死区语义禁止在变量声明前访问该变量。它强制执行命令:在声明之前不要使用任何东西。2.受TDZ影响的索赔查看受TDZ影响的索赔。(1)const变量如前所述,const变量位于声明和初始化行之前的TDZ中://can'tworkpi;//抛出`ReferenceError`constpi=3.14;我们必须在声明之后使用const变量:constpi=3.14;//Works!pi;//=>3.14(2)let变量在声明行之前,let声明语句也会被TDZ影响://can't工作计数;//抛出`ReferenceError`letcount;count=10;此外,仅在声明后使用let变量:letcount;//Works!count;//=>undefinedcount=10;//Works!count;//=>10(3)类的声明如中所示介绍,在定义类之前不能使用它://不能工作constmyNissan=newCar('red');//throws`ReferenceError`classCar{constructor(color){this.color=color;}}(4)在构造函数内部构造函数。classMuscleCarextendsCar{constructor(color,power){this.power=power;super(color);}}//不工作!constmyCar=newMuscleCar('blue','300HP');//`ReferenceError`inconstructor(),你在调用super()之前不能使用它。TDZ建议调用父构造函数来初始化实例。这样做之后,实例就可以在子构造函数中进行调整了。classMuscleCarextendsCar{constructor(color,power){super(color);this.power=power;}}//Works!constmyCar=newMuscleCar('blue','300HP');myCar.power;//=>'300HP'(5)默认函数参数默认参数存在于一个中间作用域,与全局作用域和函数作用域分开。默认参数也遵循TDZ限制。consta=2;functionsquare(aa=a){returna*a;}//不工作!square();//抛出`ReferenceError`在声明表达式a=a之前在表达式右侧使用参数a,这将生成a上的参考错误。确保在声明和初始化后使用默认参数。我们可以使用一个特殊的变量init,它在使用前被初始化:constinit=2;functionsquare(a=init){returna*a;}//Works!square();//=>43。var、function、import语句与上述语句相反,var和function定义不受TDZ影响。它们被提升到当前范围的顶部。如果您在声明var变量之前访问它,您只会得到一个未定义的变量//工作正常,但不要这样做!价值;//=>undefinedvarvalue;但是,它可以根据定义函数的位置使用://正常工作greet('World');//=>'Hello,World!'functiongreet(who){return`Hello,${who}!`;}//正常工作greet('Earth');//=>'Hello,Earth!'通常,我们通常对函数的实现不是很感兴趣,只是想调用它。因此,有时在定义函数之前调用它是有意义的。有趣的是,导入模块也被吊装了。//正常工作myFunction();import{myFunction}from'./myModule';当然建议在文件开头写import来读写方法。4.TDZ中的typeof行为typeof运算符用于判断一个变量是否定义在当前作用域中。例如,变量notDefined是未定义的。将typeof运算符应用于此变量不会引发错误:typeofnotDefined;//=>'undefined'因为变量未定义,typeofnotDefined的值未定义。但是typeof运算符在与临时死区中的变量一起使用时表现不同。在这种情况下,JS会抛出一个错误:typeofvariable;//抛出`ReferenceError`letvariable;此引用错误背后的原因是您可以静态地(仅通过查看代码)确定该变量已被定义。5.TDZ在当前范围内采取行动临时死区影响与声明语句相同范围内的变量。让我们看一个例子:functiondoSomething(someVal){//函数作用域typeofvariable;//=>undefinedif(someVal){//innerblockusesdomaintypeofvariable;//throws`ReferenceError`letvariable;}}doSomething(true);2个作用域:函数作用域定义了let变量的内部块作用域在函数作用域中,typeof变量的计算结果为未定义。在这里,let变量语句的TDZ没有作用。在内部作用域中,typeof变量语句在变量声明之前使用它,从而引发错误。ReferenceError:'variable'在初始化之前无法访问,TDZ只存在于这个内部范围内。6.总结TDZ是影响const、let和class语句可用性的一个重要概念。它不允许在声明变量之前使用变量。相反,当var变量在声明之前可以使用时,它们会继承旧的行为,应该避免。在我看来,TDZ是语言规范中的良好编码实践之一。
