TDZ
指的是 暂时性死区
,是 js 的一个概念。在这里提到这个是因为最近看到了一个很有意思的问题:函数默认值用的是 var 还是 let 来声明函数参数的?换句话说,就是解释下面的代码为什么会报 a is not defined 的错误:
00var a = 1; 01 02((a = a) => a)();
显然,如果用 var,上面的代码是不会报错的,因为:
00var a = 1; 01 02(() => { 03 var a = a; 04 return a; 05})(); 06// => undefined, var 的变量提升
因此函数默认值里,用的是 let。至于要如何理解它,还得重新看看 let 和 TDZ。
# let 和 TDZ ↵
let 和 var 做了一些类似的事,但他们还是有所不同的(这里就不讨论哪里不同了),其中一点是 js 执行 let 关键字的时候跟 TDZ 分不开,从具体的表现上来看,let 比 var 严格。比如,以下代码会报错,但 var 不会报错:
00(() => { 01 console.log(a); 02 // let a = 2; 03 // Error: a is not defined 04})();
在 js 里,上述导致报错的那段区域叫做 TDZ (代码第二行开始到 let 声明那一行)
具体的来说,当编译器发现有 let 的时候,会把变量放进 TDZ,直到 let 被执行以后才吧变量从 TDZ 中拿出来;当变量被置于 TDZ 的时候,它似乎又是存在的,只是引用他会报错:
00let a = 1; 01 02(() => { 03 console.log(a); 04 let a = 2; 05 // The Same Error: a is not defined 06})();
由此可知,在上面的匿名函数执行的时候 js 一开始就知道有 a 的存在了,当在 let a 执行之前,a 还在 TDZ 里,这时候引用它会报错,即便外层有 a 的定义。
此外,TDZ 的具体范围是到 let 的右值为止,我举个例子,你肯定能懂:
00let a = 1; 01 02(() => { 03 let a = a; 04 // Error: a is not defined 05})();
额,总之,编译器总是先执行 = 号的右边的,因此左边还没执行,也就意味着 a 依然在 TDZ 里,因此报错。
# 函数的默认参数 ↵
默认参数很方便,可以指定函数参数的默认值,比如:
00((a = 2) => a)(); 01// => 2
将上述代码稍稍改改就会得到上一节讨论的错误:
00((a = a) => a)(); 01// Error: a is not defined
那么,到底该如何理解上述的语句呢? 上述代码你可以如此理解:
00(function() { 01 // 这里为了简单说明,因此用短路写法 02 // 此处意思是: 03 // 声明 a 为第一个参数,但是: 04 // 如果第一个参数是 falsy 的 05 // 则设置为 a = a 06 // (很显然这样会踩到 TDZ 陷阱) 07 let a = arguments[0] || a; 08 return a; 09})();
因此,这个问题报的错误,你应该可以理解了吧?
00var a = 1; 01 02((a = a) => a)(); 03// Error: a is not defined