更新下相关知识,立足过往,拥抱未来。
概念
直接看规范关于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得以恢复执行。
现在,使用的是Generator,Generator函数的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定义了标识identifiers与Variables或Functions的映射。
组成 Lexical Environment包含两部分:
Environment Record记录被创建的标识identifiers与Variables或Functions的映射
| 类型 | 简述 |
|---|---|
| Declarative Environment Records | 记录 var、const、let、class、import、function等声明 |
| Object Environment Records | 与某对象绑定,记录该对象中string identifier的属性,非string identifier的属性不会被记录。Object environment records为with语句所创建 |
| Function Environment Records | Declarative Environment Records的一种,用于函数的顶层,如果为非箭头函数的情况,提供this的绑定,若还引用了 super则提供super方法的绑定 |
| Global Environment Records | 包含所有顶层声明及global object的属性,Declarative Environment Records与Object Environment Records的组合 |
| Module Environment Records | Declarative Environment Records的一种,用于ES module的顶层,除去常量和变量的声明,还包含不可变的import的绑定,该绑定提供了到另一environment records的间接访问 |
- 外部
Lexical Environment的引用 通过引用构成了嵌套结构,引用可能为null
分类 Lexical Environment分三类:
Global Environment没有外部Lexical Environment的Lexical EnvironmentModule Environment包含了模块顶层的声明以及导入的声明,外部Lexical Environment为Global EnvironmentFunction Environment对应于JavaScript中的函数,其会建立this的绑定以及必要的super方法的绑定
变量环境Variable Environments
在ES6前,声明变量都是通过var声明的,在ES6后有了let和const进行声明变量,为了兼容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规范是年年都在更新,得与时俱进的加强学习,立足过往及当下,拥抱未来!