[系列]从ECMAScript Specification中学习执行上下文之Environment Records(中)

本文接上篇从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的所有方法,只是覆盖了HasThisBindingHasSuperBinding方法的实现。同时,它还有有以下几个独有的方法:

方法名称 释义
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, HasThisBindingGetThisBinding这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可执行的操作将在下篇文章中继续介绍~

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享