es6引入了let, 解决了不少原本需要用闭包解决的问题;var与let定义的变量时,其生命周期执行的过程不尽相同。
1 变量生命周期的三部曲
当程序在使用一个变量时, 它的生命周期按以下三个阶段依次进行:
- 声明阶段(Declaration phase) : 此阶段, 在作用域中注册一个变量; //通俗的说, 就是告诉程序当前作用域有这个变量;
- 初始化阶段(Initialization phase): 此阶段, 会为变量分配内存空间, 并会自动分配数值为
undefined
; - 赋值阶段(Assignment phase): 此阶段, 会为完成初始化的变量分配一个指定的数值;
注 : 这里说的生命周期三个阶段是一个抽象的概念, 与我们通常所说的变量声明和变量初始化可能会有些出入, 对应到代码上是这样的
// 声明阶段, 当前全局作用域中存在a变量
let a; //初始化阶段, a === undefined
a = 0; //赋值阶段
{
// 开启一个块级作用域
// 声明阶段, 当前块级作用域中存在a变量
let a = 0; //初始化阶段 和 赋值阶段
}
2 var变量的生命周期
![](https://i-blog.csdnimg.cn/blog_migrate/11a52f55404fd549985388edd5031d0c.jpeg)
var变量提升: 当一个函数作用域(包括全局作用域)内, 存在用var定义的变量时, 该变量会在此作用域内所有语句执行之前完成其 声明阶段;
除此以外, 对于用var定义的变量而言, 其 声明阶段 与 初始化阶段 保持高度耦合如图所示, 即完成声明阶段紧接着就会执行初始化阶段, 自动获得值undefined
.
function test(){
// 变量a在test作用域内所有语句之前完成声明阶段和初始化阶段, 近似看成执行
// var a;
console.log(a); //undefined
var a = 0;
}
test();
var定义的变量, 其 声明阶段和初始化阶段 会被提升至所在作用域最前端执行
3 let变量的生命周期
![](https://i-blog.csdnimg.cn/blog_migrate/ea4254c5b778ff5e76b84e338f568d42.jpeg)
在开始深入了解之前, 还需知道:
let声明的变量特性
- 作用域是块级
- 同一作用域内, 无法重复声明
关于let是否会被提升, 似乎的确存在些争议. 其实, 各位说的都有道理, 只不过是每个人对 “提升” 的定义不一样罢了. 首先, 我们来看这段代码:
console.log(a); //ReferenceError
let a = 1;
可见, 用let定义的变量并不能在let语句之前获得数值, 即使像var的undefined
也没有. 这样看来, 的确是没有"提升".
然而, 还有这样一段有趣的代码:
var a = 0;
{
console.log(a); //依然是 ReferenceError !
let a;
a = 1;
console.log(a); //1
}
控制台前后打印的分别是ReferenceError 和 1. 数值1不难解释, 这里就不赘述.
那么前一个抛出的"引用错误"又是什么原因呢? 如果let定义的变量没有进行"提升", 那么当其在当前块级作用域中未捕获相应数值后, 应该会循着作用域链找到全局变量a, 打印数值0才对啊?
let变量提升: 实际上, let同var类似. 当一个块级作用域内, 存在用let定义的变量时, 该变量会在此作用域内所有语句执行之前完成其 声明阶段 ;
需要注意的是!!!
用let定义的变量 声明阶段 和 初始化阶段 是解耦的. 声明阶段位于变量所在作用域的最顶部执行, 直到let语句所在处才能执行初始化阶段. 我们将声明阶段与初始化阶段的这段区域称为 临时死区(TDZ - Temporal dead zone) , 参照 “let变量声明周期” 示意图.
因此, 也就能解释为什么会抛出ReferenceError. 块级作用域内的变量a只是在作用域顶部执行了 声明阶段, 内部处理引擎只是告诉了程序当前作用域中存在a, 并未分配其内存和数值. 而只有当a完成初始化阶段, 也就是程序运行到let所在语句才能进行正常的引用.
let定义的变量, 其 声明阶段 会被提升至所在作用域的最前端进行
4 函数声明的生命周期
![](https://i-blog.csdnimg.cn/blog_migrate/7ed56173d93354788e7809910b7456ce.jpeg)
函数声明的提升 : 函数会在其被定义的作用域最顶部完成声明,初始化和赋值三个阶段.
示例如下:
a();
function a(){
console.log("我被提升了");
a();
function a(){console.log("我又被提升了")}
}
<!--
我被提升了
我又被提升了
-->
本文参考: javascript变量的生命周期