日期:2024年11月23日

执行上下文(execution context )

变量对象(ES标准中称为词法环境)用于存储JS中的变量,变量对象所在的位置我暂且称其为作用域。于是我们遇到了一个新的问题,变量对象存变量,作用域存储变量对象。谁又来存储作用域呢?

在JS中还存在一个东西叫做执行上下文,每一个函数(包括全局作用域)都会有其对应的一个执行上下文。执行上下文在函数定义时就已创建,函数执行时所需的一切包括作用域链都存储在执行上下文中。执行上下文中存储的东西很多也很杂,不需要一一列举,你需要知道的就是执行上下文中包括函数执行所需的一切东西。

每一个函数都会有一个执行上下文,执行上下文只有在函数执行时才会起作用。在JS中还存在一个叫做调用栈的地方,调用栈专门用来存储正在执行的函数的执行上下文。

栈(stack),所谓的栈是一种数据结构。这种结构类似于枪的弹匣。

在弹匣中,先压入的子弹会在弹匣的最底部,最后压入的子弹在弹匣的最上边。开枪时最后压入的子弹最先发射,最先押入的子弹最后发射。

计算机中栈也是如此,最先存入的数据会在栈的最下边,栈最下边的对象称为栈底对象。最后存入的数据会在栈的最上边,栈最上边的对象被称为栈顶对象。获取数据时,会先获取后存入的对象,也就是栈顶对象,最先存入的对象最后才获取。

调用栈就是一个存储执行上下文的栈。调用栈会在JS加载时就会创建,创建后会将全局作用域的执行上下文放入添加近调用栈,也就是说调用栈中的栈底对象是全局作用域的执行上下文。

这一点可以通过开发这工具查看:

调用栈

通过开发者工具右侧的Call Stack就可以看到当前代码的调用栈。现在代码停在了14行,位于全局作用域,所以调用栈中只存在一个执行上下文(anonymous),anonymous意为匿名的,在这里实际上指的就是全局执行上下文。现在调用栈中只有一个执行上下文,所以它即使栈底对象又是栈顶对象对象。

简单说来,栈顶对象表示的是当前正在执行的函数。所以现在栈顶对象是全局作用域,意味着当前正在执行全局作用域中的代码。它下边的Scope显示的就是当前上下文中存储的作用域链。

现在调用一个函数,再来观察调用栈有何变化:

调用栈

现在将代码停在第9行,也就是outer函数开始执行。此时你会发现调用栈中多出来一个执行上下文outer,此时执行栈的栈顶存放的是outer的执行上下文,这意味这现在JS解析器(浏览器)正在执行outer函数,同时Scope也切换为outer函数的作用域链。

当断点打在内部函数时,调用栈又会发生新的变化:

调用栈

现在断点打到了内部函数中,代码停在了11行。在调用栈中又多出了一个匿名的调用栈,之所以显示匿名是因为内部函数没有起名字。

在调用栈中,outer的上下文已经消失,这是因为outer执行完毕,执行完毕的函数上下文会自动从调用栈中弹出。现在的栈顶的上下文是内部匿名函数的上下文,同时你会发现它的Scope也变成了内部函数的作用域链。

之所以内部函数可以访问到外部函数中的变量和参数,其实原因很简单,因为执行变量和参数早就存储到了内部函数的执行上下文中,调用内部函数时,其上下文被自动加载,与此同时其中存储的作用域链也被找到,由此便有了闭包。

关于执行上下文,我们可以暂且先了解到这样一个程度,下边我做一个简单的总结:

  1. 每一个函数(包括全局作用域)都有一个执行上下文
  2. 执行上下文中存储了当前函数执行所需的一切内容(包括作用域链)
  3. 调用栈用来存储当前正在执行或等待执行的函数的执行上下文
  4. 调用栈的栈顶对象表示当前正在执行的函数的执行上下文
  5. 执行完毕执行上下文会从栈顶弹出,然后继续执行下一个函数
0 0 投票数
文章评分
订阅评论
提醒
guest

3 评论
最旧
最新 最多投票
内联反馈
查看所有评论
超哥迷弟
超哥迷弟
1 年 前

太牛了

超哥迷弟
超哥迷弟
1 年 前

超哥,我想问问js执行机制是把所有的同步事件先全部放入栈再执行,还是放入一个执行一个

3
0
希望看到您的想法,请您发表评论x