本文接上篇从ECMAScript Specification中学习执行上下文之Environment Records(上),继续学习Environment Record。上文中我们介绍了各种不同类型的Env以及其必须要实现的抽象方法,那么本篇我们就先看一下各类Env实现这些抽象方法的具体逻辑。
由于实践中基本不使用with语句,因此 object Environment Record就不进行介绍了
Declarative Environment Record
Declarative Environment Record和ECMAScript中的某个代码作用域(scope)紧密相关,这段代码中会包含各种定义变量、常量或者函数的语法:variable, constant, let, class, module, import, function declarations,Declarative Env绑定了该范围内所声明的标识符,它对相关抽象方法的实现逻辑如下:
HasBinding(N)
如果Env中存在对名称N的绑定,返回true;否则返回false
CreateMutableBinding ( N, D )
- Assert: Env中不存在对名称N的绑定
- 为Env创建一个名称为N的可变绑定,并且状态标记为uninitialized。如果D为true,表示该绑定后续可能会被删除
- return
CreateImmutableBinding ( N, S )
- Assert: Env中不存在对名称N的绑定
- 为Env创建一个名称为N的不可变绑定,并且状态标记为uninitialized。如果S为true,则将该绑定视为一个严格绑定
- return
InitializeBinding ( N, V )
- Assert: Env中已经存在名称N的uninitialized绑定
- 将名称N的绑定值设为V
- 记录N的绑定状态变更为initialized
- return
SetMutableBinding ( N, V, S )
尝试修改绑定N的值
- If Env中不存在名称N的绑定
- If S为true,直接抛出ReferenceError
- Else 执行env.CreateMutableBinding(N, true)
- 执行env.InitializeBinding(N, V)
- return
- If N是一个严格绑定,则将S设为true
- If N绑定的状态是uninitialized,直接抛出ReferenceError
- Else If N绑定是可变的,则将其绑定值设置为V
- Else
- Assert: 试图改变一个不可变绑定的值
- 如果S为true,抛出TypeError
- return
GetBindingValue ( N, S )
- Assert: env中存在名称N的绑定
- 如果名称N的绑定是uninitialized,抛出ReferenceError
- 返回名称N的绑定值
DeleteBinding ( N )
- Assert: env中存在名称N的绑定
- 如果名称N的绑定无法被删除,return false
- 移除env中名称N的绑定
- return true
HasThisBinding ( )
- return false
普通的Declarative Environment Record并不提供this绑定,但是其子类型function Environment Record 和 module Environment Record是存在this绑定的,这一点后面会提及
HasSuperBinding ( )
- return false
WithBaseObject ( )
- return undefined
Function Environment Record
Function Environment Record是Declarative Environment Record的子类型,而且从字面意思上也不难看出,它代表的是一个函数的顶层作用域,还有两点需要注意:
- 如果函数不是箭头函数,那么env还提供了this绑定(这一点对应了箭头函数最大的特性:没有自己的this值,其this值绑定的是创建函数时所在作用域的this值)
- 如果不是箭头函数,并且内部引用了super,那么env还包含了super方法调用所需要的状态(这一点对应的是class的继承语法中引用super的情况,因为ECMAScript中的class本质都是function)
与父类型Declarative Environment Record相比,Function Environment Record有以下几个额外的属性:
字段名称 | 可选值 | 释义 |
---|---|---|
[[ThisValue]] | Any | 函数调用时的this值 |
[[ThisBindingStatus]] | lexical 或 initialized 或 uninitialized | 如果为lexical,表示函数是一个箭头函数,没有本地this值 |
[[FunctionObject]] | Object | env被创建时所调用的函数 |
[[NewTarget]] | Object 或 undefined | 如果env是由于内部的 [[Construct]]内部方法调用所创建的,那么[[NewTarget]]就指向 [[Construct]]方法的newTarget参数(这种情况其实意味着函数是用new操作符所调用的,此时[[NewTarget]]指向的就是构造函数) |
Function Environment Record继承了Declarative Environment Record的所有方法,只是覆盖了HasThisBinding
和 HasSuperBinding
方法的实现。同时,它还有有以下几个独有的方法:
方法名称 | 释义 |
---|---|
BindThisValue(V) | 设置上文中提到的[[ThisValue]]属性值为V |
GetThisBinding() | 获取绑定的this值 |
GetSuperBase() | 返回super属性获取的base值 |
被覆盖的2个方法和独有的3个方法的具体实现逻辑如下:
BindThisValue ( V )
- Assert: env的[[ThisBindingStatus]]不是lexical
- If: env的[[ThisBindingStatus]]是initialized,抛出ReferenceError
- 将env的[[ThisValue]]设置为V
- 将env的[[ThisBindingStatus]]设置为initialized
- return V
HasThisBinding ( )
- 如果env的[[ThisBindingStatus]]是lexical,返回false;否则返回true
HasSuperBinding ( )
- If: env的[[ThisBindingStatus]]是lexical,返回false
- If: env的[[FunctionObject]].[[HomeObject]]为undefined,返回false;否则返回true
这里要补充一下函数对象的[[HomeObject]]属性,这是ECMAScript函数对象的内置属性之一,其值是Object类型,含义为:If the function uses super, this is the object whose [[GetPrototypeOf]] provides the object where super property lookups begin.
其中[[GetPrototypeOf]] 等同于Object.getPrototypeof方法
从含义中可以看出,当函数中使用了super进行属性获取或者方法调用时,[[HomeObject]]的隐式原型对象就是查找这些属性或方法的初始对象。此时主要是用于class继承的语法,因为在继承语法中,super的使用是非常多样的,具体可以参考阮老师的文章es6.ruanyifeng.com/#docs/class…
GetThisBinding ( )
- Assert: env的[[ThisBindingStatus]]不是lexical
- If: env的[[ThisBindingStatus]]是uninitialized,抛出ReferenceError
- return evn.[[ThisValue]]
GetSuperBase ( )
- let home = env.[[FunctionObject]].[[HomeObject]]
- If: home为undefined,则直接return undefined
- Assert: typeof home 为 Object
- return home的隐式原型对象
Module Environment Record
Module Environment Record是Declarative Environment Record的另外一个子类型,它代表是一个ES Module的外层作用域.(关于ES Module同样也可以参考阮老师的文章:es6.ruanyifeng.com/#docs/modul…)
除普通的可变和不可变绑定外,Module Environment Record还提供了不可变的import绑定,这些绑定可以使当前Module获得其他Env中的绑定(简而言之,就是Module作用域中可以通过import语法引入外部的绑定,并且这些绑定是不可变)
Function Environment Record继承了Declarative Environment Record的所有方法,但是覆盖了GetBindingValue
, DeleteBinding
, HasThisBinding
和 GetThisBinding
这4个方法,同时新增了CreateImportBinding和GetThisBinding方法。
新增的2个方法的含义见下表:
方法名称 | 释义 |
---|---|
CreateImportBinding(N, M, N2) | 创建一个间接的不可变绑定,N是绑定名称,M是一个Module Record,N2是M中存在的一个绑定(Module Record也是标准内部的一个数据结构,我们这里可以间接理解为是一个模块就可以) |
GetThisBinding() | 获取env的this绑定 |
覆盖的4个和独有的2个方法的执行逻辑如下:
CreateImportBinding ( N, M, N2 )
为env创建一个不可变的间接绑定,之后获取这个绑定值时会通过指针获取M中的N2绑定值。
这里其实引出了ES Module的一个重要特性,即ES Module输出的是值的引用,因为在导入的模块中获取这个值时,实际是到目标Module中去取值的
- Assert: env当前不存在名称N的绑定
- Assert: M是一个Module Record
- Assert: M中存在对N2的直接绑定
- 为env创建一个名称为N的间接绑定,并且引用M中的N2绑定作为其目标绑定,同时记录绑定状态为initialized
- return
GetBindingValue ( N, S )
- Assert: S 为true(这里对应的含义是:ES Module是自动开启严格模式的)
- Assert: env存在名称N的绑定
- If: 绑定是一个间接绑定
- 获取间接绑定的M和N2
- 获取M中的env作为targetEnv
- 如果targetEnv是undefined,抛出ReferenceError
- 返回targetEnv.GetBindingValue(N2, true)
- env中名称N的绑定状态是uninitialized,抛出ReferenceError
- return env中的名称N的绑定
DeleteBinding ( N )
Module Environment Record中的DeleteBinding方法是不会被使用的
HasThisBinding ( )
返回 true
GetThisBinding ( )
返回undefined
从上面几个方法的行为中可以看出,Module Environment Record的最大不同就是增加了导入外部绑定值的能力,可以通过import语句来实现;此外,还可以知道,在ES Module的外层作用域中,this值始终是undefined
Global Environment Record和Environment Record可执行的操作将在下篇文章中继续介绍~