「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」
前述
对于变量提升这个词眼,想必大家一定会脱口而出,用var声明变量会造成变量提升,那么let呢?你有想过let到底有没有变量提升呢?变量提升的背后到底是什么?下面开始表演⊙0⊙
变量的生命周期
在Javascript引擎使用变量时,它包含三个阶段:
- 声明阶段
在作用域范围内注册变量 - 初始化阶段
为变量分配内存并在作用域内创建绑定关系,自动初始化为undefined - 分配阶段
为初始化变量赋于一个值
什么是变量提升?
所谓的变量提升,所有的变量声明都会被拉到函数作用域的顶部,就是说我一个函数内部声明变量代码的前面就可以使用这个变量,不会抛出语法错误,看下面这个例子:
function fn() {
console.log(name,age);
var name = 'gsk';
var age = 27;
}
fn(); // undefined undefined;
为什么我们能够输出name和age为undefined而不会报错呢,明明我们没有在使用之前做变量的声明? 这是因为ECMAScript运行时会像这样解析上面这段代码:
function fn() {
var name;
var age;
console.log(name,age);
name = 'gsk';
age = 27;
}
fn(); // undefined undefined;
我们来解释下这段代码,Javascript代码在执行之前其实是有一个预编译的过程,预编译时如果遇到var就会将var声明的变量提升到当前作用域的最前面,即(var name; var age)提升到我们fn函数当前作用域的顶部,然后开始执行代码输出console,给name、age进行赋值操作;
简单一句话总结:变量提升就是我们能在变量声明之前使用变量而不会报错,注意了,这是适用于var;
let会造成变量提升吗?为什么?
答案是会,但是与var产生的效果不一样,同样我们来看个let和var对比的例子:
var
if(true) {
console.log(name); // undefined;
var name = 'gsk';
console.log(name); // gsk
}
console.log(name); // gsk
let
if(true) {
console.log(name); // Uncaught ReferenceError: Cannot access 'name' before initialization;
let name = 'gsk';
console.log(name); // gsk
}
console.log(name); // undefined;
不加let
if(true) {
console.log(name); // undefined; if块中没有会向上找到全局,默认未声明的的变量属于window,所以是undefined;
}
我们知道,var的变量声明会被提升至函数作用域或全局作用域的顶部,所以无论在if内部前后和外部都可以输出name不会报错,if和花括号形成的作用域是一个块作用域,即let声明所在的是一个块级作用域;
let声明所在的块级作用域即离let向上一层最近的一组花括号(function、if-else 代码块或 for 循环中)
Javascript引擎在执行预编的时候也会注意到在if块中的let声明,但是在声明之前不能以任何方式来引用该变量,这个不能引用的瞬间,即console.log到let name这个执行瞬间被称为“暂时性死区”;
虽然有暂时性死区的存在导致我们访问不了let声明之前的变量,但是我们比较let和不加let两块,let块中未声明之前报的错误是不能得到一个未初始化的值,那说明已经有了变量声明的提升,只是没有初始化而已;
代入生命周期总结
var的变量提升提升了两个阶段,声明并初始化为undefined;
let的变量提升只提升了声明阶段,没有到达初始化阶段;
所以,我们得出结论let会造成变量提升,虽然因为暂时性死区的存在,导致我们不能够在let声明之前来引用未声明的let变量,但是let确实造成了变量声明的提升,只是没到初始化阶段,会抛出未初始化的错误。