这是一篇关于执行上下文(Execution Context) && 作用域链(Scop chain)的笔记。
Execution Context
词法环境(Lexical environment)
有两个功能:
环境记录 (environment record) : 储存变量和函数声明
- 是一种 标识符-变量映射(identifier-variable mapping) 的结构。
- 标识符(identifier):变量/函数(variable/function)的名称
- 变量(value):是对对象(object)或原始值(primitive)的引用。\
- 是一种 标识符-变量映射(identifier-variable mapping) 的结构。
指向外界: outer (reference to the outer(parent) environment)
lexicalEnvironment = { environmentRecord: { <identifier> : <value>, <identifier> : <value> } outer: < Reference to the parent lexical environment> thisBinding:取决于运行所在地 }
Code:
var apple = 10; function foo() { var banana = 20; console.log('foo'); } foo();
GlobalLexicalEnvironment = { environmentRecord: { apple : '10', foo : < reference to function object > } outer: null } FunctionLexicalEnvironment = { environmentRecord: { banana : 20, } outer: <globalLexicalEnvironment> }
变量环境(Variable environments)
The LexicalEnvironment and VariableEnvironment components of an execution context are always Lexical Environments. When an execution context is created its LexicalEnvironment and VariableEnvironment components initially have the same value. The value of the VariableEnvironment component never changes while the value of the LexicalEnvironment component may change during execution of code within an execution context.
https://262.ecma-international.org/5.1/#sec-10.3
从中可知LexicalEnvironment 和 VariableEnvironment指向的都是Lexical Environments。
执行上下文(Execution context)
- 执行JavaScript代码的环境
执行上下文的种类:
- 全局执行上下文(Global Execution Context / GEC)
- 最基本的Excution Context,且只有一个
- 当代码加载时,默认进入的执行环境
- 创建一个内存堆来存储variables和function的引用地址
- variables的最初值皆为”undefined”
- 函数执行上下文(Function Execution Context / FEC)
- 可以有多个
- 每当调用一个函数时,会新建一个函数执行上下文。
执行上下文分为两个阶段:
创建阶段
执行阶段
创建阶段:
JS Engine 会解析代码,对代码中的
var
变量和函数提升到代码顶层,并开辟空间进行储存,所有的var
变量初始值皆为undefined
,所以可以在声明之前被引用。 这就会引出变量提升(
Hoisting
)的问题:console.log(apple); // undefined foo(); // foo var apple = 10; function foo() { console.log('foo'); }
console.log(apple)
的结果为undefined
就是因为变量声明,在执行第一行代码时apple = undefined
,直到第三行var apple = 10
时apple
才会被赋值于10
变量提升的规则:- 作用于
var
,会被初始化为undefined
- 对于
let
const
,不会被初始化有默认值,所以会报错: ·ReferenceError : is not defined
创建作用域链(The Scope Chain)
当JS引擎在自己的环境里找不到所要的变量时,就会在外界环境(outer environment)中寻找,也就是词法环境中的outer, 直到找到为止。但是外部作用域不能作用于内部函数
找到 identifier,则返回值或引用,若没有赋值就返回
undefined
未找到 identifier,则返回报错并打印
Uncaught ReferenceError: <identified> is not defined
执行阶段:
执行堆栈 (Execution Stack)
- 堆栈是存储以后进先出(LIFO)形式的值的数据结构
- 当函数执行调用时,都会从堆栈中移除,但是其词法环境是否从内存中释放,取决于其是否被其它词法环境的
outer
引用 - 每当调用一个新的函数时,就会新建一个Execution Context放到Execution Context Stack的顶端
- 作用于
Example
Code Example 1
var apple = 10;
var banana = 20;
function foo() {
console.log('foo');
}
function bar() {
var num = 3
console.log('bar');
foo()
}
bar()
//Output:
//bar
//foo
对于变量来说,在编译的时候JS引擎会会将变量的值初始化为“undefined”,直到开始执行到变量值的那一行代码,才会将真正的值赋值给变量
Code Example 2
function b(){
var myvar
console.log(myvar)
}
function a(){
var myvar = 2
console.log(myvar)
b()
}
var myvar = 1
console.log(myvar)
a()
console.log(myvar)
编译的时候,Global Object中会存储有变量和函数,变量的值为"undefined",函数则指向此函数所在的位置