前端面试题攻略2-let是否会造成变量提升?

290 阅读3分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

前述

对于变量提升这个词眼,想必大家一定会脱口而出,用var声明变量会造成变量提升,那么let呢?你有想过let到底有没有变量提升呢?变量提升的背后到底是什么?下面开始表演⊙0⊙

变量的生命周期

在Javascript引擎使用变量时,它包含三个阶段:

  1. 声明阶段 在作用域范围内注册变量
  2. 初始化阶段为变量分配内存并在作用域内创建绑定关系,自动初始化为undefined
  3. 分配阶段为初始化变量赋于一个值

什么是变量提升?

所谓的变量提升,所有的变量声明都会被拉到函数作用域的顶部,就是说我一个函数内部声明变量代码的前面就可以使用这个变量,不会抛出语法错误,看下面这个例子:

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确实造成了变量声明的提升,只是没到初始化阶段,会抛出未初始化的错误。