关于前端:温故而知新-重新认识JavaScript的Execution-Context

2次阅读

共计 4385 个字符,预计需要花费 11 分钟才能阅读完成。

更新下相干常识,立足过往,拥抱将来。

概念

间接看标准对于 ExecutionContext 的定义:

An execution context is a specification device that is used to track the runtime evaluation of code by an ECMAScript implementation.

ExecutionContext为抽象概念,用来形容可执行代码的执行环境。可执行代码的运行,都是在 ExecutionContext 中。

治理形式ExecutionContextStack

Execution context Stack为后进先出(LIFO)的栈构造。栈顶永远是 running execution context。当管制从以后execution context 对应可执行代码转移到另一段可执行代码时,相应的 execution context 将被创立,并压入栈顶,执行完结,对应的 execution context 从栈顶弹出。

考虑:什么 ECMAScript 个性会使 Execution context stack 不遵循 LIFO 规定?

标准外面提到:

Transition of the running execution context status among execution contexts usually occurs in stack-like last-in/first-out manner. However, some ECMAScript features require non-LIFO transitions of the running execution context.

而后在标准外面,并没有找到 some ECMAScript features 到底是什么个性。不过,第一反馈,Generator算不算?在 stackoverflow 上,有这么一个探讨 Execution Context Stack. Violation of the LIFO order using Generator Function

function *gen() {
  yield 1;
  return 2;
}

let g = gen();

console.log(g.next().value);
console.log(g.next().value);

调用一个函数时,以后 execution context 暂停执行,被调用函数的 execution context 创立并压入栈顶,当函数返回时,函数 execution context 被销毁,暂停的 execution context 得以复原执行。

当初,应用的是 GeneratorGenerator 函数的 execution context 在返回 yield 表达式的值之后依然存在,并未销毁,只是暂停并移交出了控制权,可能在某些时候复原执行。

到底是不是呢?有待求证。

词法环境Lexical Environments

看标准的定义:

A Lexical Environment is a specification type used to define the association of Identifiers to specific variables and functions based upon the lexical nesting structure of ECMAScript code.

按标准来说,Lexical Environment定义了标识 identifiersVariablesFunctions 的映射。

组成 Lexical Environment蕴含两局部:

  • Environment Record
    记录被创立的标识 identifiersVariablesFunctions 的映射
类型 简述
Declarative Environment Records 记录 varconstletclassimportfunction等申明
Object Environment Records 与某对象绑定,记录该对象中 string identifier 的属性,非 string identifier 的属性不会被记录。Object environment recordswith 语句所创立
Function Environment Records Declarative Environment Records的一种,用于函数的顶层,如果为非箭头函数的状况,提供 this 的绑定,若还援用了 super则提供 super 办法的绑定
Global Environment Records 蕴含所有顶层申明及 global object 的属性,Declarative Environment RecordsObject Environment Records 的组合
Module Environment Records Declarative Environment Records的一种,用于 ES module 的顶层,除去常量和变量的申明,还蕴含不可变的 import 的绑定,该绑定提供了到另一 environment records 的间接拜访
  • 内部 Lexical Environment 的援用
    通过援用形成了嵌套构造,援用可能为null

分类 Lexical Environment分三类:

  • Global Environment
    没有内部 Lexical EnvironmentLexical Environment
  • Module Environment
    蕴含了模块顶层的申明以及导入的申明,内部 Lexical EnvironmentGlobal Environment
  • Function Environment
    对应于 JavaScript 中的函数,其会建设 this 的绑定以及必要的 super 办法的绑定

变量环境Variable Environments

在 ES6 前,申明变量都是通过 var 申明的,在 ES6 后有了 letconst进行申明变量,为了兼容 var,便用Variable Environments 来存储 var 申明的变量。

Variable Environments本质上仍为Lexical Environments

机制

具体能够参考标准 ECMAScript 2019 Language Specification。相干的是在8.3 Execution Contexts

一篇很不错的文章参考Understanding Execution Context and Execution Stack in Javascript,该文章的中文翻译版中文版

参考外面的例子:

var a = 20;
var b = 40;
let c = 60;

function foo(d, e) {
    var f = 80;
    
    return d + e + f;
}

c = foo(a, b);

创立的 Execution Context 像这样:

GlobalExecutionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      c: < uninitialized >,
      foo: < func >
    }
    outer: <null>,
    ThisBinding: <Global Object>
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // Identifier bindings go here
      a: undefined,
      b: undefined,
    }
    outer: <null>, 
    ThisBinding: <Global Object>
  }
}

在运行阶段,变量赋值曾经实现。因而 GlobalExecutionContext 在执行阶段看起来就像是这样的:

GlobalExecutionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      c: 60,
      foo: < func >,
    }
    outer: <null>,
    ThisBinding: <Global Object>
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // Identifier bindings go here
      a: 20,
      b: 40,
    }
    outer: <null>, 
    ThisBinding: <Global Object>
  }
}

当遇到函数 foo(a, b) 的调用时,新的 FunctionExecutionContext 被创立并执行函数中的代码。在创立阶段像这样:

FunctionExecutionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      Arguments: {0: 20, 1: 40, length: 2},
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>,
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      f: undefined
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>,
  }
}

执行完后,看起来像这样:

FunctionExecutionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      Arguments: {0: 20, 1: 40, length: 2},
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>,
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      f: 80
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>,
  }
}

在函数执行实现当前,返回值会被存储在 c 里。因而 GlobalExecutionContext 更新。在这之后,代码执行实现,程序运行终止。

总结

ECMAScript标准是年年都在更新,得与时俱进的增强学习,立足过往及当下,拥抱将来!

参考资料

  1. 所有的函式都是閉包:談 JS 中的作用域與 Closure
  2. JS 夯实之执行上下文与词法环境
  3. 联合 JavaScript 标准来谈谈 Execution Contexts 与 Lexical Environments
  4. You-Dont-Know-JS 2nd-ed
  5. ECMAScript 2019 Language Specification
  6. Understanding Execution Context and Execution Stack in Javascript
正文完
 0