【底层原理】聊聊执行上下文与执行栈

1. 什么是执行上下文?

JavaScript是解释执行语言,但是在执行代码前,js引擎会做一些准备工作来创建代码的执行环境,所有的代码都是在执行上下文中运行的。

执行上下文(execution context 简称 EC ) 就是代码的执行环境

执行上下文 为我们的可执行代码块提供的准备工作有:

  • 变量对象的定义
  • 作用域链的扩展
  • this绑定

2. 执行上下文有哪些类型

  1. 全局执行上下文

这个是程序最基础的执行上下文,任何不在函数内部的代码都在全局上下文中。全局上下文生成会做两件事:

  1. 创建一个全局对象(在浏览器中是window,而Node中是global)
  2. 让this指向这个全局对象
复制代码
  1. 函数执行上下文

    每当函数调用的时候,会创建一个函数执行上下文(重复调用这个函数会产生新的执行上下文)

  2. Eval 函数执行上下文

    执行在 eval 函数内部的代码也会有它属于自己的执行上下文,但由于我们并不经常使用 eval,所以在这里我们不讨论它

3. 执行上下文中的内容?

在 JavaScript 代码执行前的预编译处理阶段,执行上下文会被创建,主要会发生下面3件事:

  1. This 绑定
  2. 创建词法环境组件(LexicalEnvironment component
  3. 创建变量环境组件。(VariableEnvironment component

所以执行上下文在概念上表示如下:

ExecutionContext = {
  ThisBinding = <this value>,
  LexicalEnvironment = { ... },
  VariableEnvironment = { ... },
}
复制代码

3.1 词法环境

简单来说 词法环境 是一种持有 标识符—变量映射 的结构。这里的 标识符 指的是变量/函数的名字,而 变量 是对实际对象(包含函数类型对象)或原始数据的引用。

3.1.1 词法环境的内容?

在词法环境的内部有两个组件:

(1) 环境记录器:存储变量和函数声明的实际位置

环境记录器也有两种类型:
1.  声明式环境记录器:存储变量、函数和参数。
2.  对象环境记录器:用来定义出现在全局上下文中的变量和函数的关系。
复制代码

(2) 一个外部环境的引用:通过其访问父级词法环境(作用域)

3.1.2 词法环境的类型

(1)全局词法环境

全局词法环境是没有外部环境引用的词法环境。全局环境的外部环境引用是 null。它拥有内建的 Object/Array/等、在环境记录器内的原型函数(关联全局对象,比如 window 对象)还有任何用户定义的全局变量,并且 this的值指向全局对象。

(2)函数词法环境

函数内部用户定义的变量存储在环境记录器中。并且引用的外部环境可能是全局环境,或者任何包含此内部函数的外部函数。

我们可以总结得出:

  • 全局环境中,环境记录器是对象环境记录器。
  • 函数环境中,环境记录器是声明式环境记录器。

3.2 变量环境

变量环境 它也是一个 词法环境 ,所以它有着词法环境的所有特性。

单独分出这个变量环境的概念是为 ES6 服务的:

ES6 中,词法环境组件和 变量环境 的一个不同就是前者被用来存储函数声明和变量(letconst)绑定,而后者只用来存储 var 变量绑定

3.3 this

在全局执行上下文中,this 的值指向全局对象。(在浏览器中,this引用 Window 对象)。

在函数执行上下文中,this 的值取决于该函数是如何被调用的。如果它被一个引用对象调用,那么 this 会被设置成那个对象,否则 this 的值被设置为全局对象或者 undefined(在严格模式下)

let foo = {
  baz: function() {
  console.log(this);
  }
}
foo.baz();   // 'this' 指向 'foo', 因为 'baz' 被对象 'foo' 调用
let bar = foo.baz;

bar();       // 'this' 指向全局 window 对象,因为没有指定引用对象

复制代码

4. 执行上下文栈

执行栈,也就是在其它编程语言中所说的“调用栈”,是一种拥有LIFO(后进先出) 数据结构的栈,被用来存储代码运行时创建的所有执行上下文。

当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部。

引擎会执行那些执行上下文位于栈顶的函数。当该函数执行结束时,执行上下文从栈中弹出,控制流程到达当前栈中的下一个上下文。

执行上下文栈.png

这是不是也能说明为什么外部作用域不能访问内部作用域的原因呢?

❤码字不易点个免费的赞吧❤

猜你喜欢

转载自juejin.im/post/7050839597579927560