关于本文内容均基于es5标准
执行环境
执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。
执行环境分为:
1)全局执行环境,最外层执行环境,在所有代码未开始执行前就已经创建完毕,页面关闭后销毁。
2) 函数执行环境。函数被调用时创建,函数执行完毕后销毁。
3) Eval函数执行环境。
在第五版中,间接调用 eval 函数会将全局对象作为执行代码的变量环境和词法环境。
“直接”调用是指函数调用涉及eval标识符,而其他方式都算作间接调用。
一个执行环境包括三个组件:this绑定、词法环境组件、变量环境组件。
词法环境对象
每个词法环境对象包含两部分:
- 环境记录器
- 外部环境的引用(可能为空,比如全局词法环境)
环境记录器
环境记录器分为两种:
1)声明式环境记录器
存在于函数作用域中,存储变量、函数、参数。
2)对象式环境记录器
存在于全局作用域和块级作用域中,存储变量、函数。
外部环境引用
如果在当前环境内找不到变量,引擎可以通过引用在外部环境继续查找。
变量环境组件与词法环境组件
执行环境的词法环境和变量环境组件始终为 词法环境对象。
区别是:
let、const声明的变量,外部环境引用保存在词法环境组件中。
var和function声明的变量和保存在环境变量组件中。
以下面代码为例:
let a = 1;
const b = 2;
var c = 3;
function test (d, e) {
var f = 10;
return f * d * e;
}
c = test(a, b);
解析阶段的全局环境内的词法环境和变量环境
GlobalLexicalEnvironment = {
LexicalEnvironment: {
OuterReference: null,
EnviromentRecord: {
Type: 'object',
a: ,
b:
},
},
VariableEnvironment: {
EnviromentRecord: {
type: 'object',
test: ,
c: undefined,
}
}
}
解析test时的词法环境和变量环境
GlobalLexicalEnvironment = {
LexicalEnvironment: {
OuterReference: null,
EnviromentRecord: {
Type: 'object',
a: 1 ,
b: 2
},
},
VariableEnvironment: {
EnviromentRecord: {
type: 'object',
c: 3,,
test:
}
}
}
FunctionLexicalEnvironment = {
LexicalEnvironment: {
OuterReference: ,
EnviromentRecord: {
Type: 'Declarative',
arguments: {0: 1, 1: 2, length: 2}
},
},
VariableEnvironment: {
EnviromentRecord: {
Type: 'Declarative',
f: undefined,
}
}
}
执行完毕后的词法环境和变量环境
GlobalLexicalEnvironment = {
LexicalEnvironment: {
OuterReference: null,
EnviromentRecord: {
Type: 'object',
a: 1 ,
b: 2 ,
test:
},
},
VariableEnvironment: {
EnviromentRecord: {
type: 'object',
c: 20,
}
}
}
FunctionLexicalEnvironment = {,
LexicalEnvironment: {
OuterReference: ,
EnviromentRecord: {
Type: 'Declarative',
arguments: {0: 1, 1: 2, length: 2}
},
},
VariableEnvironment: {
EnviromentRecord: {
Type: 'Declarative',
f:10,
}
}
}
------------- 分割线 -----------------
作用域
决定了一段代码能够访问到那些数据,这些数据存放在词法环境对象内。
作用域链
当执行一个函数时,引擎会将函数的执行环境推入执行栈并生成作用域链。
作用域链的顶部永远是当前环境的词法环境对象,下一个词法环境对象来自于包含环境,下下个词法环境对象来自于包含环境的包含环境。作用域链的尾部是全局词法环境对象。
作用域链是一个链表的形式。当我们在一个函数内部调用某一个变量时,会从作用域链顶部开始查找。如果在当前执行环境内存在定义则返回,否则会在当前环境的词法环境组件中引用的外部环境引用中查找。然后重复前面的逻辑,直到找到全局词法环境对象(外部引用环境为null)如果仍然未找到会抛出异常。