这几节课程中我们讲到了变量提升,但是你有没有想过 JavaScript 是如何实现变量提升的,ES6 中又是如何通过 let、const 实现块级作用域的,这节内容揭晓答案。
在上节课程 执行上下文与调用栈 我们学习了执行上下文。在 JavaScript 主要有两种情况会创建执行上下文(其实还有一种,暂且不讲),一种是全局 JavaScript 代码,另一种是函数。而「变量提升」和「块级作用域」主要依托于执行上下文。
分析一段代码,代码中有使用 var 和 let 申明的变量:
function look() {
var name = '素燕';
let age = 19;
if (age > 18) {
let money = 0;
var from = 'home';
if(money <= 0) {
money += 10;
let isNeedMore = money <= 10;
if (isNeedMore) {
money += 10;
let isEnd = money > 10;
var canGo = isEnd;
}
isNeedMore = false;
}
money = 0;
} else {
let needAge = 18 - age;
var add = age + needAge;
}
}
look();
1. 创建全局执行上下文并压入调用栈,全局只定义了一个函数 look;

2.创建函数 look 的执行上下文;
第一步:
把通过 var 申明的变量加入到变量环境中,并初始化为 undefined。使用 var 声明的变量有 name、from、canGo、addd。把 let 声明的变量加入到词法环境中,let 申明的变量属于块级作用域,此时在当前块中只有 age。在词法环境中,利用栈来管理不同的块级作用域,当有新的块级作用域时会入栈,块级作用域中的代码执行完后会进行出栈操作。

此时的调用栈为:

第二步:
当执行到第 5 句的时候,此时出现了一个新的块。创建一个新的块。此时只有一个变量 money,加入词法环境中。需要强调一点,块级作用域的变量是在代码块要执行时才会被加入到词法环境中,块与块之间相互独立,通过栈来管理同一个执行上下文的块。

第三步
执行到第 8 句的时候,又遇到一个块,此时有一个变量 isNeedMore,加入到词法环境中。

第四步

第五步

第六步
块级作用域中的块依次出栈。当 look 函数执行完成后,look 执行上下文从调用栈中出栈。最终调用栈只剩下了全局执行上下文。
总结
本文结合执行上下文分析了变量提升与块级作用域的实现,变量提升其实就是在编译阶段把var声明的变量注入到变量环境中,而块级作用域的实现其实是通过不同的块来保存块中使用let、const 声明的变量,通过栈的机制来处理不同的块。执行上下文对理解 this,闭包有很大的作用。大家加油。
推荐阅读:
