前言
简单来说,执行上下文是一个抽象概念,表示JS代码在运行时的环境,我个人更喜欢叫执行环境,感觉更加明确一些。
但可能涉及到一些翻译的问题,在一般的技术文章中,上下文(context)和环境(environment)的区别是,context强调特定情况的背景(环境),更强调其关联性,而environment主要指的是由基本的物质组成的背景(环境)。
context一般指某段程序或某个模块的运行环境等,environment指的是某个程序的载体运行环境,在未作说明的情况下,JS的执行环境可能会被理解为浏览器环境。
什么是执行上下文?
执行上下文是帮助于我们和理解和评估JS引擎是如何执行JS代码的一个抽象概念。它表示的是JS代码在执行时的环境。
执行上下文分为三种:
-
全局执行上下文 —— 全局执行上下文是在JS引擎执行JS代码时创建的,全局执行上下文会创建一个全局的 window 对象(在浏览器的情况下),并且设置
this的值等于这个全局对象。一个程序中只会有一个全局执行上下文。 -
函数执行上下文 —— 每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序(将在后文讨论)执行一系列步骤。
-
Eval 函数执行上下文 — 执行在
eval函数内部的代码也会有它属于自己的执行上下文,但由于 JavaScript 开发者并不经常使用eval,所以在这里不会讨论它。
执行栈(堆栈)
执行栈,在其他编程语言中也称为“调用栈”,是一个具有 LIFO(后进先出)结构的堆栈,用于存储代码在执行过程中创建的所有执行上下文。
当 JavaScript 引擎第一次遇到您的脚本时,它会创建一个全局执行上下文并将其压入当前执行栈。每当JS引擎找到函数调用时,它都会为该函数创建一个新的执行上下文并将其压入栈顶部。
引擎会执行栈顶部的函数。当这个函数完成时,它将从当前执行栈中弹出,并且当前执行上下文环境同时也会切换到它下一个函数。
因为JS引擎是单线程的,所以采用了这种后进先出的结构,并且在执行代码的过程中,永远只会有一个执行上下文环境在运行状态,如果是在函数内部,那么当前执行上下文就是此函数的上下文。
全局上下文会在脚本完全结束的时候被弹出(浏览器关闭)
我们可以通过代码来示例来理解一下执行栈
let a = 'Hello World!';
function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
function second() {
console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');
当上述代码在浏览器中加载时,JS引擎会创建一个全局执行上下文并将其推送到当前执行栈。当遇到调用first()时,JS引擎会为该函数创建一个新的执行上下文,并将其推送到当前执行堆栈的顶部。
当从first()函数内部调用second()函数时,Javascript 引擎会为该函数(second函数)创建一个新的执行上下文,并将其推送到当前执行堆栈的顶部。当second()函数结束时,它的执行上下文从当前栈中弹出,控件到达它下面的执行上下文,也就是first()函数执行上下文。
当first()完成时,它的执行堆栈从堆栈中移除并且控制到达全局执行上下文。上下文在其所有代码都执行完毕后会被销毁,包括定义在它上面的所有变量和函数(全局上下文在应用程序退出前才会被销毁,比如关闭网页或退出浏览器)
注:参考链接1这里说的是执行完所有代码后全局执行上下文就会被删除,没有做额外的解释,这里是不对的。
参考
《Javascript高级程序设计第4版》4.2