作为前端开发者,有很多必备基础需要知道比如(提升机制、作用域、作用域链)。这些我的主页有文章进行讲解,有兴趣可以一起讨论学习。正确理解执行上下文和执行栈的概念将有助于你成为一名更好的 JavaScript 开发人员
切入正题,下面介绍执行上下文和执行栈
一句话定义
执行上下文是JavaScript运行时的一个重要概念,它是代码被执行时所处的环境。这个环境包含了代码执行所需要的信息,如变量、函数以及它们的作用域等。
执行上下文三种类型
- 1.全局上下文
全局上下文是在浏览器的window环境下为全局变量(如使用var声明的变量)创建的执行环境。当JavaScript代码首次被解析时,会先进行短暂的编译,创建全局环境,并将其压入执行栈的最底层。全局上下文在整个程序生命周期内始终存在,直到浏览器窗口关闭
- 2.函数上下文
每次函数被调用时,JavaScript引擎会为该函数创建一个新的执行上下文。这个上下文不仅包含了函数内部声明的所有变量和参数,还形成了一个局部作用域。每当调用一个函数fn,对应的函数执行上下文会被压入执行栈的顶部。当函数执行完毕后,该上下文从栈顶弹出,控制权返回到上一个执行上下文
- 3.Eval上下文
执行在 eval 函数内部的代码也会有它属于自己的执行上下文,但由于 JavaScript 开发者并不经常使用 eval,所以在这里我不会讨论它。
执行上下文内容
执行上下文主要包括三个部分:执行代码、词法环境和变量环境。
- 执行代码:这是实际运行的JavaScript代码。
- 词法环境:包含函数内部声明的变量和函数,以及作用域链信息。
- 变量环境:包含使用
var声明的变量和函数声明。
当V8引擎对代码进行短暂编译后,会在全局作用域中创建全局上下文。每当调用一个函数时,会创建一个函数上下文,并将其动态压入执行栈。执行代码在词法环境或变量环境中运行,通过作用域链查找变量或函数。如果当前作用域中找不到变量或函数,JavaScript引擎会沿着作用域链向上查找,直到找到为止或到达全局上下文。执行完成后,当前的执行上下文从栈中弹出,进行垃圾回收。
可以用一个链式的结构来理解这个过程: js 代码执行 <- 执行机制(执行栈) <- 函数入栈(操作系统)<- 执行上下文(代码和变量声明的关系) <- 作用域 + 变量提升+ 作用域链 <- 全局上下文
执行栈
在编程语言中,执行栈(也称为调用栈)是一种使用LIFO(后进先出)数据结构的栈,用于存储代码运行时创建的所有执行上下文。JavaScript作为一种动态语言和脚本语言,能够在运行时动态地更新执行栈。
每当调用一个函数时,该函数的执行上下文会被压入执行栈的顶部,而全局执行上下文始终位于栈底。当函数执行完毕后,其执行上下文从栈顶弹出,控制权返回到上一个执行上下文。这种机制确保了函数调用的正确顺序和作用域链的完整性。
注意:
- 函数声明优先于变量声明。在每个执行上下文中,所有函数声明会被提升到顶部,然后按顺序处理变量声明。
- 执行栈的管理确保了代码的正确执行顺序,同时也支持JavaScript的动态特性和异步编程模型。
创建执行上下文
在了解了JavaScript引擎如何管理执行上下文之后,接下来我们将深入探讨JavaScript引擎是如何创建执行上下文的。
执行上下文的创建分为两个阶段:1)创建阶段;2)执行阶段。
1. 创建阶段
在创建阶段,JavaScript引擎会执行以下步骤:
- This Binding
- 即确定this的值
- 在全局执行上下文中,
this的值指向全局对象,即在浏览器中指向window对象。 - 在函数执行上下文中,
this的值取决于函数的调用方式。如果函数作为对象的方法被调用,this将指向该对象;否则,在非严格模式下,this指向window对象,而在严格模式下,this为undefined。代码例如:
let student = {
name: 'peter',
birthYear: 2000,
calcAge: function() {
console.log(2024 - this.birthYear);
}
}
person.calcAge();
// 'this' 指向 'person', 因为 'calcAge' 是被 'person' 对象引用调用的。
let calculateAge = person.calcAge;
calculateAge();
// 'this' 指向全局 window 对象,因为没有给出任何对象引用
-
词法环境(Lexical Environment)
-
根据ES6官方文档,词法环境是一种规范类型,用于定义标识符与特定变量和函数的关联关系。词法环境由环境记录(environment record)和可能为空引用(null)的外部词法环境组成。
-
简而言之,词法环境是一个包含标识符变量的集合结构。标识符指的是变量或函数的名称。
-
词法环境包含两个主要部分:
- 环境记录(Environment Record) :存储变量和函数声明的实际位置。
- 对外部环境的引用:允许访问其外部词法环境。
-
-
变量环境(Variable Environment)
- 变量环境主要用于存储使用
var声明的变量和函数声明。在创建阶段,这些声明会被初始化,但变量的值默认为undefined,而函数声明会被提升并赋值。
- 变量环境主要用于存储使用
2. 执行阶段
在执行阶段,JavaScript引擎会执行以下操作:
- 执行代码:逐行执行代码,根据需要读取和修改变量的值。
- 作用域链:如果在当前执行上下文中找不到某个变量或函数,JavaScript引擎会沿着作用域链向上查找,直到找到该变量或函数,或者到达全局上下文。
- 垃圾回收:当执行上下文中的代码执行完毕后,该上下文从执行栈中弹出,相关的变量和函数会被垃圾回收机制回收。
示例代码分析
考虑以下示例代码:
var a = 1;
function fn(a) {
var a = 2;
var funA = function a() {};
// 函数优先
var b = a;
console.log(funA); //打印 function
console.log(a);//打印 2 ,想想为啥不是3
}
fn(3);
- 创建阶段
-
全局执行上下文
this绑定到window对象,创建词法环境和变量环境,包含变量a,初始值为undefined,再 初始化a的值为1。 -
函数
fn的执行上下文this绑定到window对象(假设非严格模式)。- 创建词法环境和变量环境,包含参数a,初始值为undefined。传入参数(即a = 3)初始化参数a的值为3
var a = 2;声明变量a,初始值为undefined,初始化 a = 2。- 声明函数
funA,初始值为function a() {}。 - 声明变量
b,初始值为undefined,初始化为 a 。
- 执行阶段
-
全局执行上下文 调用函数
fn(3),将fn的执行上下文压入执行栈。 -
函数
fn的执行上下文- 执行代码,首先将
a的值从3重新赋值为2。 - 声明函数
funA,值为function a() {}。 - 将
b的值赋为a的值,即2。
- 执行代码,首先将
-
全局执行上下文
- 当
fn执行完毕后,其执行上下文从执行栈中弹出。 - 全局执行上下文继续执行后续代码,直到所有代码执行完毕,全局执行上下文从执行栈中弹出,执行栈为空。
- 当
总结
通过上述分析,我们可以看到JavaScript引擎在创建和执行执行上下文时的详细步骤。这些步骤确保了代码的正确执行顺序和作用域链的完整性。希望这些内容能帮助您更好地理解JavaScript的执行上下文机制。有篇大佬文章将三种执行上下文环境讲的很nice,推荐给大家
收藏等于学会,点赞等于~~(脑补中)
后续推出闭包以及其他JavaScript相关文章,敬请期待~~