阅读理解:Understanding Execution Context and Execution Stack in Javascript
先贴原文地址:Understanding Execution Context and Execution Stack in Javascript
算是一个对 Javascript 基础的查漏补缺吧,内容主要包含 js 的执行上下文和执行栈。同样,并不是全文翻译,而是主要内容概括(就是搬运自己的阅读笔记 )。
执行上下文(Execution Context)
执行上下文 分为三类:
- Global Execution Context:全局执行上下文。基础的执行上下文,所有不被包含在函数内部的代码均在全局执行上下文中。其主要做两件事情:创建一个全局对象(以浏览器环境为例则是 window 对象);设置
this
的值为该全局对象。全局执行上下文在程序中仅存在一个; - Functional Execution Context:函数执行上下文。当函数被执行时,一个函数执行上下文将会被创建。每一个函数都有其自己的函数执行上下文,但其仅函数在被执行或被调用的时候创建。函数执行上下文的可能存在多个;
- Eval Function Execution Context:eval 函数执行上下文。其只存在于 eval 代码执行中,较为少见。
执行栈 / 调用栈(Execution Stack / Call Stack)
执行栈,也就是调用栈,以 LIFO 的形式被存储,存储了代码执行过程中所有的执行上下文。
直接看一个例子:
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');
这个例子展示了执行栈在代码执行过程的变化,内容一目了然,就不在这里赘述了,接下来是一些更进阶的原理相关内容。
执行上下文是如何被创建的
执行上下文的创建分为两个阶段:
- Creation Phase
- Execution Phase
Creation Phase
创建阶段主要包含三部分工作:
- Value of this is determined, also known as This Binding
- LexicalEnvironment component is created
- VariableEnvironment component is created
This binding
关键字 this 的绑定也就是确认 this 的引用对象,在 global context 中,this 指向 global 对象,在 function context 其指向调用函数的对象。 // 也就是 this 的基本规则
LexicalEnvironment component
根据 ES6 的官方声明,LexicalEnvironment 的定义如下:
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. A Lexical
Environment consists of an Environment Record and a possibly null
reference to an outer Lexical Environment.
翻译过来就是:Lexical Environment(词汇环境?) 是一种规范的类型,其用于定义特定变量的标识符与基于ECMAScript代码词法嵌套结构函数之间的关联。Lexical Environment 由环境记录和外部词汇环境的可能空引用组成。// emmmmm…听起来很复杂
简单来讲:Lexical Environment 就是 标识符——变量 的 mapping 结构。(标识符指向 变量/函数 名,同时 变量 是对于真实对象的引用。) // 好吧,还是不懂
Lexical Environment 分为两个部分:
- the environment record:环境记录就是变量和函数声明真实存储的地方;
- a reference to the outer environment:对外部环境的引用意味着其能访问到父词法环境(作用域)
Lexical Environment 有两种,分别为:
- global environment:对应着全局执行上下文,其没有外部环境,也就是对外部环境的引用为 null。其拥有 built-in 的 Object/Array/etc… 原型方法
- function environment:用户定义的变量记录在 environment record 中,同时外部环境的引用可能为 global 可能为其他 function
environment record 也有两种,分别为:
- Object environment record:定义 global context 中变量和函数的关联,对应着 global environment
- Declarative environment record:存储变量/函数/参数,对应着 function environment
这里有一些伪代码来帮助你理解:
GlobalExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
}
outer: <null>
}
}
FunctionExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
}
outer: <Global or outer function environment reference>
}
}
Variable Environment
Variable Environment 也是一种 Lexical Environment,因此具有前述的所有性质。
ES6 中,两者的区别在于:Lexical Environment 存储函数声明和变量(let/const 的绑定),Variable Environment 只存储变量(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",
// Identifier bindings go here
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >
}
outer: <null>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
c: undefined,
}
outer: <null>
}
}
FunctionExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GlobalLexicalEnvironment>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
g: undefined
},
outer: <GlobalLexicalEnvironment>
}
}
注意:这里的 FunctionExectionContext 只有在 multiple 函数被调用才生成。
可以很明显的看到两者区别:let 和 const 需要初始化,而 var 已经赋了 undefined 的值,其也导致 var 的变量提升。
Execution Phase
执行阶段就是完成所有变量的赋值操作以及最终执行函数。
注意,如果 let 的变量无法再声明时找到赋值,其会被赋 undefined 值。