JS-执行上下文

183 阅读3分钟

1. 什么是执行上下文?

简而言之,每当JavaScript代码在运行时,都会做一个准备工作,这个准备工作就是执行上下文。

2. 执行上下文的类型

  • 全局执行上下文 这是默认的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:1. 创建一个全局对象,2.设置this的值等于这个全局对象。
  • 函数执行上下文 每当一个函数调用时,都会创建一个上下文,按照顺序执行。
  • Eval函数执行上下文 不常用

3. 执行栈

当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');

image.png

4. 创建执行上下文

在创建阶段有三件事:

  1. This绑定
  2. 创建词法环境组件
  3. 创建环境变量组件

1. This绑定

  • 全局执行上下文:this指向全局对象。
  • 函数执行上下文:this指向被调用的对象。如果没被调用,则指向全局。

2. 创建词法环境

1. 什么是词法环境?

词法环境就是持有 标识符-变量映射的结构。(名-值)

2. 词法环境的内部组件

  • 环境记录器:存储变量/函数的声明位置
  • 外部环境引用: 可以访问父级词法环境(作用域)

3. 词法环境的类型

全局环境包括

  • 内建的Object/Array
  • 在(对象)环境记录器中的原型函数和自定义的全局变量。 外部词法环境为null

函数环境包括

  • 在(声明式)环境记录器中的函数内部自定义的变量。
  • 声明式环境记录器中还包含了一个传递函数的arguments对象和length属性。 外部词法环境为全局/包含此函数的函数。

伪代码如下:

全局执行上下文 = {
  词法环境: {
    对象环境记录器: {
      Type: "Object",
      // 在这里绑定标识符
    }
    outer: <null>
  }
}

函数执行上下文 = {
  词法环境: {
    声明式环境记录器: {
      Type: "Declarative",
      // 在这里绑定标识符
    }
    outer: <Global or outer function environment reference>
  }
}

3. 创建变量环境

变量环境其实也是一个词法环境,在ES6中的一个不同就是变量环境存储var变量绑定的数据。观察下面代码来理解如上概念。

let a = 20;
const b = 30;
var c;

function multiply(e, f) {
 var g = 20;
 return e * f * g;
}

c = multiply(20, 30);

它的执行上下文如下:

GlobalExectionContext = {

  ThisBinding: <Global Object>,

  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // 在这里绑定标识符
      a: < uninitialized >,
      b: < uninitialized >,
      multiply: < func >
    }
    outer: <null>
  },

  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // 在这里绑定标识符
      c: undefined,
    }
    outer: <null>
  }
}

FunctionExectionContext = {
  ThisBinding: <Global Object>,

  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // 在这里绑定标识符
      Arguments: {0: 20, 1: 30, length: 2},
    },
    outer: <GlobalLexicalEnvironment>
  },

VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // 在这里绑定标识符
      g: undefined
    },
    outer: <GlobalLexicalEnvironment>
  }
}

我们可以看到用letconst定义的变量没有关联值,而var定义的变量设置成了undefined。这就是var为什么有变量提升。

5. 执行

完成对所有变量的分配,执行代码。