let x = 10; let foo = () => { console.log(x); let x = 20; x++; } foo();
实际上 let
也是存在变量提升的
let x = 10; let foo = () => { // 这里访问 x 就会在当前作用域内找 x // 如果没有下面的 let x = 20 // 当前作用域就找不到,然后就会继续找外层变量,最后输出 10 // 但是下面有声明,声明会提升到当前代码块最前面 // 这个时候 x 还没有初始化,存在暂存死区中,访问就会抛出 ReferenceError console.log(x); let x = 20; x++; }; foo();
如果把里面的 let
换成 var
就不会报错,显示 undefined
就是因为 let
是在编译时才会初始化,var
在声明时就会初始化
let x = 10; let foo = () => { console.log(x); let x = 20; x++; } foo();
let foo = () => { let x; console.log(x); //throws ReferenceError x = 20; x++; }
refs:
当程序的控制流程在新的作用域(module, function或block作用域)进行实例化时,在此作用域中的用let/const声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定,也就是对声明语句进行求值运算,所以是不能被访问的,访问就会抛出错误。所以在这运行流程一进入作用域创建变量,到变量开始可被访问之间的一段时间,就称之为TDZ(暂时死区)。
【摘自JavaScript高级程序设计】
let与var的另一个重要的区别,就是let声明的变量不会在作用域中被提升。
// name会被提升 console.log(name); // undefined var name = 'Matt'; // age不会被提升 console.log(age); // ReferenceError:age没有定义 let age = 26;
在解析代码时,JavaScript引擎也会注意出现在块后面的let声明,只不过在此之前不能以任何方式来引用未声明的变量。在let声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此阶段引用任何后面才声明的变量都会抛出ReferenceError。
let a = 1 { a = 2 let a }这个一样会报错Reference Error 变量不能在初始化之前被访问
考点:let的暂时性死区
暂时性死区:ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。
凡是在声明之前就使用这些变量,就会报错。
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了(类似变量提升),但是不可获取(与变量提升的区别)。
只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
if (true) { //只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,获取会报错 // 暂时性死区开始 tmp = 'abc'; // ReferenceError console.log(tmp); // ReferenceError let tmp; // 暂时性死区结束 //只有等到声明变量的代码出现,才可以获取和使用该变量 console.log(tmp); // undefined tmp = 123; console.log(tmp); // 123 }
let x = 10; let foo = () => { console.log(x); x++; } foo();以上代码正常输出10毫无疑问。
let x = 10; let foo = () => { let x; console.log(x); x++; } foo();
let x = 10; let foo = () => { console.log(x); let x = 20; x++; } foo();