# 执行上下文
当执行到一个函数的时候,就会进行准备工作,这里的“准备工作”, 就叫做"执行上下文(execution context)"。然后会将函数执行上下文压入执行上下文栈。
# 执行上下文
执行上下文是 JavaScript 执行一段代码时的运行环境。每个执行上下文,都有重要的属性:
- 变量环境。
- 词法环境。
- 可执行代码。
- 作用域链。
- this。
# 变量提升。
foo();
myName;
var myName = 'heihei';
function foo() {
console.log('嘿嘿');
}
按照我们的正常的逻辑来说,这其实是会报错的.
- 执行函数 foo ,找不到 foo 就报错。
- 执行 myName ,找不到就报错。
所以我们可以得出一个结论。
- 变量定义之前就使用它,该值为 undefined。
- 函数定义之前使用它,会正常执行函数。
变量提升:
是指在 JavaScript 代码执行过程中,JavaScript 引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为。变量被提升后,会给变量设置默认值,这个默认值就是我们熟悉的 undefined。
但是变量和函数在代码的位置是不会变的,而是在编译阶段被JavaScript 引擎放入内存中。
# 变量环境。
当进入执行上下文时,这时候还没有执行代码。
变量对象会包括:
函数的所有形参 (如果是函数上下文)
- 由名称和对应值组成的一个变量对象的属性被创建。
- 没有实参,属性值设为 undefined.
函数声明。
- 由名称和对应值组成一个变量对象的属性被创建。
- 如果变量对象已经存在相同名称的属性,则完全替换这个属性。
变量声明
- 由名称和对应值 (undefined),组成一个变量对象的属性被创建。
- 如果变量名称根已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。
js 会把声明以外的代码编译为字节码(可执行的代码)。
# 词法环境
在 ES6 之前,ES 的作用域只有两种:全局作用域和函数作用域。
变量提升所带来的问题:
- 变量容易在不被察觉的情况下被覆盖掉
- 本应销毁的变量没有被销毁
ES6 是如何解决变量提升带来的缺陷,ES6 引入了 let 和 const 关键字。
function foo(){
var a = 1
let b = 2
{
let b = 3
var c = 4
let d = 5
console.log(a)
console.log(b)
}
console.log(b)
console.log(c)
console.log(d)
}
foo()
接下来我们就来一步步分析上面这段代码的执行流程。
第一步是编译并创建执行上下文
在词法环境内部,维护了一个小型栈结构, 栈底是函数最外层的变量,进入一个作用域块后,就会把该作用域块内部的变量压到栈顶,当作用域执行完成之后,该作用域的信息就会从栈顶弹出,这就是词法环境的结构。
变量查找过程

# 作用域链
函数在创建的时候有一个内部属性 [[scope]] ,里面保存所有父变量对象到其中, [[scope]] 就是所有父变量对象的层级链,但是并不是完整的作用域链子。
在每个执行上下文的变量环境中, 会将活动对象添加到作用链的前端。
Scope = [当前函数作用域].concat([[scope]])
当一段代码使用了一个变量时,JavaScript 引擎首先会在“当前的执行上下文”中查找该变量。
如果在当前的变量环境中没有查找到,那么 JavaScript 引擎会继续在 outer 所指向的执行上下文中查找。
如果在 bar 函数或者 foo 函数中使用了外部变量,JavaScript 引擎会去全局执行上下文中查找。我们把这个查找的链条就称为作用域链。
foo 函数调用的 bar 函数,那为什么 bar 函数的外部引用是全局执行上下文,而不是 foo 函数的执行上下文?
这是因为词法作用域(看词法作用域章节)。
# 执行阶段
JavaScript 引擎开始执行“可执行代码”,按照顺序一行一行地执行。
看一个更加完整的变量环境。
function foo(a) {
var b = 2;
function c() {}
var d = function() {};
b = 3;
}
foo(1);
在进入执行上下文之后:

代码执行

# 最终的函数的生命周期
foo 函数被创建,保存作用域到内部属性 [[scope]].
foo 函数被执行,创建 foo 函数执行上下文,foo 函数执行上下文被压入执行上下文栈。
foo 并不会立即执行,先会复制函数 [[scope]] 创建作用域链.
引擎把变量的声明部分和函数的声明部分。来收集变量环境和词法环境。
将环境压入 foo 作用域链顶端.
生成可执行代码。
开始执行函数,修改变量环境、词法环境的属性值。
返回后函数执行完毕,函数从上下文栈中弹出。