一文弄懂JS执行上下文

执行上下文

执行上下文(Execution context)的概念在 JavaScript 中是颇为重要的。变量或函数的执行上下文决定了它们可以访问哪些数据,以及它们的行为。抽象地讲,可以理解为一个运行环境盒子 ,JavaScript 代码在这个盒子里运行。

每个执行上下文都有一个关联的变量对象 (variable object),这个执行上下文中定义的所有变量和函数都存在于这个对象上,除此之外,还包含作用域链和 this 关键字的值。

JavaScript 中有两种类型的执行上下文:

  • 全局执行上下文(Global Execution Context - GEC)
  • 函数执行上下文(Function Execution Context - FEC)

上下文在其所有代码都执行完毕后被销毁,包括定义在它上面的所有变量和函数(GEC 在应用退出前才会被销毁,比如关闭网页或退出浏览器)。让我们结合如下代码详细了解一下两者。

js 复制代码
var a = 10;
function greetings() {
  let word = "hello";
  function addExtra(str1, str2) {
    return str1 + str2;
  }
  return addExtra("hello", "world");
}
greetings();

全局执行上下文

GEC 是基础的默认的执行上下文,执行所有不在函数内部的 JavaScript 代码

根据 ECMAScript 实现的宿主环境,表示全局执行上下文的对象可能不一样。在浏览器中,GEC 就是我们常说的 window 对象,因此所有通过 var 定义的全局变量和函数都会成为 window 对象的属性和方法,而使用 let 和 const 的顶级声明不会定义在 GEC 中。

对于每个JavaScript宿主环境,只能有一个全局上下文。

函数执行上下文

每当函数调用时,JavaScript 引擎都会在全局上下文中创建不同类型的执行上下文,称为函数执行上下文。 由于每个函数都有自己的函数执行上下文(一个函数不一定只有一个,因为)

执行上下文的创建

执行上下文(GEC 或 FEC)的创建分两个阶段进行:

  1. Creation Phase 创建阶段
  2. Execution Phase 执行阶段

Creation Phase 创建阶段

在创建阶段,执行上下文首先与执行上下文对象(Execution Context Object - ECO)相关联。创建阶段又分为3个阶段,在此期间定义和设置执行上下文对象的属性。这些阶段是:

  1. 创建变量对象(Variable Object - VO)
  2. 创建作用域链(Scope Chain)
  3. 设置this关键字的值

让我们详细回顾下每个阶段。

创建变量对象

前文提到,变量对象是在执行上下文中创建的类似对象的容器。它存储在该执行上下文中定义的变量和函数声明。 在 GEC 中,对于使用 var 关键字声明的每个变量,都会向 VO 中添加一个指向该变量并设置为 "undefined" 的属性。

此外,对于每个函数声明,都会向 VO 中添加一个指向该函数的属性,并且该属性存储在内存中。这意味着所有函数声明都将在 VO 中存储和访问,甚至在代码开始运行之前。

因为在代码执行之前就将变量和函数声明存储在内存中,由此引发了提升

我们用伪代码来表示:

js 复制代码
GEC = {
  VO: {
    a: undefined,
    greetings: function greetings() { ... }
  }
}

greetingsFEC = {
  VO: {
    word: nothing, // 注意这里不是 undefined
    addExtra: function greetings() { ... }
  }
}

创建作用域链

作用域链是当前作用域中可访问的变量对象列表。作用域中的每个变量对象都表示更高级别的作用域。

用伪代码表示:

js 复制代码
GEC = {
  VO: {
    a: undefined,
    greetings: function greetings() { ... }
  },
  scope: [ ...GEC.VO]
}

greetingsFEC = {
  VO: {
    word: nothing, // 注意这里不是 undefined
    addExtra: function greetings() { ... }
  },
  scope: [ GEC.scope, ...greetings.VO]
}

设置 "this"

创建阶段里最后一步是设置 this 关键字的值。Javascript 中 this 关键字是指执行上下文所属的范围。创建作用域链后,JS 引擎将初始化 this 的值。

在 GEC 中,this 指的是全局对象,即 window 对象(浏览器环境)。

对于 FEC,它不会创建 this 对象,相反,它可以访问它所定义的环境。通俗地讲,是指向调用者,因此不会创建对象啦。

Execution Phase 执行阶段

这是实际代码执行开始的阶段。在此之前,VO 中包含值为 undefined 和 nothing 变量,如果运行代码则必然要更新为实际值。然后,代码由解析器解析,转换为可执行的字节码,最后执行。

执行上下文栈

执行上下文栈(Execution Context Stack)跟踪在脚本生命周期内创建的所有执行上下文。因为 JavaScript 是单线程语言,这意味着它一次只能执行一个任务。因此,当其他操作、函数和事件发生时,将为每个事件创建一个执行上下文,再将要其放入执行上下文栈中。如下图所示:

请注意这个栈中执行顺序,后进先出。

相关推荐
Tirzano1 分钟前
vue3 ts 简单动态表单 和表格
前端·javascript·vue.js
程序员_三木9 分钟前
使用 Three.js 创建圣诞树场景
开发语言·前端·javascript·ecmascript·three
赵大仁38 分钟前
深入理解 Vue 3 中的具名插槽
前端·javascript·vue.js·react.js·前端框架·ecmascript·html5
秋雨凉人心8 小时前
简单发布一个npm包
前端·javascript·webpack·npm·node.js
哥谭居民000110 小时前
将一个组件的propName属性与父组件中的variable变量进行双向绑定的vue3(组件传值)
javascript·vue.js·typescript·npm·node.js·css3
踢足球的,程序猿10 小时前
Android native+html5的混合开发
javascript
前端没钱10 小时前
探索 ES6 基础:开启 JavaScript 新篇章
前端·javascript·es6
一条不想当淡水鱼的咸鱼11 小时前
taro中实现带有途径点的路径规划
javascript·react.js·taro
土豆炒马铃薯。12 小时前
【Vue】前端使用node.js对数据库直接进行CRUD操作
前端·javascript·vue.js·node.js·html5
温轻舟12 小时前
前端开发 -- 自动回复机器人【附完整源码】
前端·javascript·css·机器人·html·交互·温轻舟