执行上下文
执行上下文的概念
执行上下文,也可以称为执行环境,其实就是 JavaScript 代码在运行前,创建的一块内存空间,其中包含了代码运行所需要的内容。这个过程,我们称为预编译。
JavaScript 执行上下文分为三种:
- 全局执行上下文,它是为运行代码主体而创建的执行上下文,也就是说它是为那些存在于函数之外的所有代码而创建的
- 函数执行上下文,每个函数在执行的时候都会创建自己的执行上下文
- eval 函数执行上下文(不推荐使用),使用 eval 函数也会创建一个新的执行上下文
创建执行上下文
JavaScript 代码运行时,首先会进入全局环境,对应的会创建全局执行上下文。而代码中基本都会存在函数,只要调用一次函数,就会创建一个该函数的执行上下文。
创建执行上下文的步骤如下:
确定 this 指向
- 在全局执行上下文中,this 指向 window 对象
- 在函数执行上下文中,this 指向 window 对象
- 在对象中调用函数,this指向该对象
- 使用new关键字调用函数,this指向对象实例
创建变量对象(Variable Object)
- 确定函数的形参值和特殊变量
arguments - 确定普通字面量形式的函数声明
- 确定变量声明、函数表达式声明
- 确定函数的形参值和特殊变量
执行栈
在 JavaScript 中,通过栈的存取方式来管理执行上下文,我们称之为执行栈(Call Stack)。
栈遵循先进后出,后进先出的规则,也称为 LIFO(Last In First Out)规则。
接下来我们来分析 JavaScript 中如何通过栈来管理通过执行上下文:
代码执行进入一个执行环境时,它的执行上下文就会被创建,并被推入执行栈中(入栈)
代码执行完成时,它的执行上下文就会被销毁,并从栈顶被推出(出栈),控制权交由下一个执行上下文
因为 JavaScript 在执行代码时最先进入全局环境,所以处于栈底的永远是全局环境的执行上下文。而处于栈顶的是当前正在执行函数的执行上下文。
当函数调用完成后,它就会从栈顶被推出,理想的情况下,闭包会阻止该操作。
而全局环境只有一个,对应的全局执行上下文也只有一个,只有当页面被关闭之后它才会从执行栈中被推出,否则一直存在于栈底。
下面我们来看一段具体的代码示例:
function foo () {
function bar () {
return 'I am bar';
}
return bar();
}
foo();对应图解如下:

执行上下文可存在多个,虽然没有明确的数量限制,但如果超出栈分配的空间,会造成堆栈溢出。常见于递归调用,没有终止条件造成死循环的场景。
// 递归调用自身
function foo() {
foo();
}
foo();
// 报错: Uncaught RangeError: Maximum call stack size exceeded总结
执行上下文,其实就是 JavaScript 代码在运行前,创建的一块内存空间,其中包含了代码运行所需要的内容。
JavaScript 执行上下文分为三种:
- 全局执行上下文,它是为运行代码主体而创建的执行上下文,也就是说它是为那些存在于函数之外的所有代码而创建的
- 函数执行上下文,每个函数在执行的时候都会创建自己的执行上下文
- eval 函数执行上下文(不推荐使用),使用 eval 函数也会创建一个新的执行上下文
JavaScript 代码运行时,进入全局环境或调用函数时,都会创建对应的执行上下文。
创建执行上下文的步骤如下:
确定 this 指向
- 在全局执行上下文中,this 指向 window 对象
- 在函数执行上下文中,this 指向 window 对象
- 在对象中调用函数,this 指向该对象
- 使用 new 关键字调用函数,this 指向对象实例
创建变量对象(Variable Object)
- 确定函数的形参值和特殊变量
arguments - 确定普通字面量形式的函数声明
- 确定变量声明、函数表达式声明
- 确定函数的形参值和特殊变量
JavaScript 通过执行栈来管理执行上下文,栈的特点是先进后出,后进先出。
这代表着全局执行上下文始终在栈底,当前正在执行函数的函数执行上下文始终在栈顶,函数执行完毕后,就会从栈顶被推出。