首先来看一段代码
console.log(a) var a = 2;
输出结果是2,正常来说JS如果是逐行向下执行,那么应该输出undefined,为何此处输出2呢?
原因在于JS在执行前都会进行编译(通常就在执行前),在编译过程中包括变量和函数在内的所有声明都会被处理。
定义
是指在 JavaScript 代码执行前的编译过程中,JavaScript 引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置默认undefined。
那let,const是否会进行变量声明提升呢?
首先我们要知道定义一个JS变量分为三个阶段
- 创建create
- 初始化initialize
- 赋值assign
下面我们分别来看看var、let、function 和 const的过程
var声明
function fn() { var x = 1; } fn();
- 进入 fn,为 fn 创建一个环境。
- 找到 fn 中所有用 var 声明的变量,在这个环境中「创建」这些变量(即 x 和 y)。
- 将这些变量「初始化」为 undefined。
- 开始执行代码
- x = 1 将 x 变量「赋值」为 1
由以上步骤可知,var 声明会在代码执行之前就将创建变量,并将其初始化为 undefined。即创建和初始化会被提升
function声明
fn(); function fn() { console.log(1); }
- 找到所有用 function 声明的变量,在环境中「创建」这些变量。
- 将这些变量「初始化」并「赋值」为 function(){ console.log(1) }。
- 开始执行代码 fn2()
由以上步骤可知,function 的「创建」「初始化」和「赋值」都被提升了
let声明
{ let x = 1; x = 2; }
- 找到所有用 let 声明的变量,在环境中创建这些变量
- 开始执行代码(注意现在还没有初始化)
- 执行 x = 1,将 x 初始化为 1(这并不是一次赋值,如果代码是 let x,就将 x 初始化为 undefined)
- 执行 x = 2,对 x 进行赋值
由以上步骤可知,let只有创建过程会提升,初始化和赋值都不会提升,所以会形成暂时性死区,这也是为什么在定义前使用let会抛错。
const声明
{ console.log(x); const x = 1; }
- 找到所有用 const声明的变量,在环境中「创建」这些变量
- 开始执行代码(注意现在还没有初始化)
- 执行x = 1, 将x初始化为1
由以上步骤可知,const的创建过程也会提升,但是与let不同之处在于const只有创建和初始化两个过程,没有赋值过程。若不初始化则会直接抛错
总结
- var 的创建和初始化都被提升了。
- function 的创建、初始化和赋值都被提升了。
- let 的创建被提升了,但是初始化和赋值没有提升。
- const的创建被提升但是初始化没提升。const没有赋值。
注意⚠️
函数优先
何为函数优先,来看一下下面的代码👇
foo(); var foo; function foo() { console.log(1) }
实际上输出值会是1,因为当变量声明和函数声明同时存在时,函数声明优先于变量声明,即函数声明会覆盖变量声明。原因在于函数的赋值过程也会提升。那有人可能有疑问,如果将var变成let呢?结果为foo不允许被重复声明。
变量提升的存储位置
执行上下文中存在一个变量环境的对象(Viriable Environment),该对象中保存了变量提升的内容。而let,const则存在词法环境中。