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

685 阅读7分钟

接上篇文章[系列]从ECMAScript Specification中学习执行上下文之Environment Records(中) ,本篇继续介绍Object Environment Record和Global Environment Record

Object Environment Record

Object Environment Record的最核心特点在于其拥有一个绑定对象(binding object),object env绑定了该对象上的所有字符串属性作为env中的标识符,从而可以在相应的scope中直接使用这些标识符,非字符串的属性不会被绑定为标识符。

  • 绑定对象本身和继承的属性均会被env绑定为标识符
  • 绑定对象属性的增减也会导致env的标识符绑定的变化,所有通过新增绑定对象属性所导致的均为可变绑定(即使该属性是不可写的)

object env有以下几个专有的字段:

字段名称可选值释义
[[BindingObject]]对象即上文中提到的binding object
[[IsWithEnvironment]]布尔用于表示该env是不是为with语句所创建的

Object Environment Record方法的执行逻辑

HasBinding ( N )

  • let: bindingObject = env.[[BindingObject]]
  • let: foundBinding = HasProperty(bindingObject, N)
  • if: foundBinding 为 false, return false.
  • if: envRec.[[IsWithEnvironment]] is false, return true
  • let: unscopables = Get(bindingObject, @@unscopables)
  • if: Type(unscopables) 为 Object, 则:
    • let: blocked = ToBoolean(? Get(unscopables, N))
    • if: blocked 为 true, return false
  • return true

Get()方法可以简单理解为获取对象的某个属性值,具体可以参考:tc39.es/ecma262/#se…

@@unscopables是一个内置的Symbol,具体可以参考developer.mozilla.org/zh-CN/docs/…

CreateMutableBinding ( N, D )

  • let: bindingObject = env.[[BindingObject]]
  • return DefinePropertyOrThrow(bindingObject, N, PropertyDescriptor { [[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }).

DefinePropertyOrThrow可以粗略理解为Object.defineProperty方法,具体可以参考:tc39.es/ecma262/#se…

CreateImmutableBinding ( N, S )

在object env中不会被使用

InitializeBinding ( N, V )

  • 直接调用 env.SetMutableBinding(N, V, false)

SetMutableBinding ( N, V, S )

  • let: bindingObject = env.[[BindingObject]]
  • let: stillExists = HasProperty(bindingObject, N)
  • if: stillExists 为 false,并且 S 为 true, 直接抛出 ReferenceError
  • return Set(bindingObject, N, V, S)

Set可以粗略理解为一个对象写入属性,具体描述可参考标准:tc39.es/ecma262/#se…

GetBindingValue ( N, S )

  • let: bindingObject = env.[[BindingObject]]
  • let: value = HasProperty(bindingObject, N)
  • if: value为 false, 则:
    • if: S为 false, return undefined; 否则抛出ReferenceError
  • return Get(bindingObject, N)

DeleteBinding ( N )

在object env中,只有绑定名称N所对应的bind object中的属性是[[Configurable]] = true时才可以删除

  • let: bindingObject = env.[[BindingObject]]
  • return bindingObject.[[Delete]] (N)

[[Delete]]方法的逻辑可参考标准:tc39.es/ecma262/#se…

HasThisBinding ( )

  • return false

HasSuperBinding ( )

  • return false

WithBaseObject ( )

  • if: env.[[IsWithEnvironment]]为true,return env.[[BindingObject]]
  • else: return undefined

Global Environment Record

Global Environment Record代表的是最外层的作用域,它提供了以下几种绑定:内置的全局对象,全局对象的属性和script元素内的所有顶层声明(注意:这里的script元素指的应该是非module的普通script元素)

Global Environment Record在逻辑上是一个单独的env,但是在标准定义中,它其实是一个 object Environment Recorddeclarative Environment Record的组合体。

  • object Environment Record包含了全部的内置全局变量,我们代码中经常会出现的NaN,undefined,Infinity,还有parseInt, parseFloat方法,等等,这些都是包含在这个Environment Record的绑定之中的。结合with语句的使用(with语句所创建的就是一个object env),我们就可以明白在代码中为什么可以直接写NaN,而不需要写global.NaN。这些所有的属性或方法可以参考标准第19章(tc39.es/ecma262/#se…)

  • 除了这些内置的全局变量之外,object Environment Record中还包括以下这些语法所声明的绑定:函数声明generator函数声明异步函数声明(async)异步generator函数声明var声明

  • 除了上述的情况之外,其他在全局环境中的声明都包含在了declarative Environment Record中,例如全局环境下的let, const,class语句。

Global Environment Record的特有字段和方法

Global Environment Record有以下几个额外的字段:

字段名称可选值释义
[[ObjectRecord]]Object Environment Record即上文中提到的object Environment Record
[[GlobalThisValue]]Object全局作用域中的this值,宿主环境可能提供任何对象作为this值
[[DeclarativeRecord]]Declarative Environment Record上文中提到的declarative Environment Record
[[VarNames]]String[]全局作用域下由函数声明generator函数声明异步函数声明(async)异步generator函数声明var声明所产生的绑定名称的列表

Global Environment Record额外拥有的方法如下:

方法名称释义
GetThisBinding()返回env的this绑定值
HasVarDeclaration (N)判断env中是否存在名称为N,并且是通过函数声明generator函数声明异步函数声明(async)异步generator函数声明var声明所创建的绑定
HasLexicalDeclaration (N)判断env中是否存在名称为N,并且是通过词法声明所创建的绑定。词法声明包括let, const, class等
HasRestrictedGlobalProperty (N)判定名称N是否是全局的一个受限属性
CanDeclareGlobalVar (N)判定CreateGlobalVarBinding(N)能否调用成功
CanDeclareGlobalFunction (N)判定CreateGlobalFunctionBinding(N)能否调用成功
CreateGlobalVarBinding(N, D)下文单独介绍
CreateGlobalFunctionBinding(N, V, D)下文单独介绍

这里先重点介绍一下CreateGlobalVarBinding(N, D)和CreateGlobalFunctionBinding(N, V, D)这两个方法:

- CreateGlobalVarBinding(N, D)

用于在object Environment Record组件中创建并初始化名称为N的绑定,初始值为undefined。该绑定是一个可变绑定,如果D为true,代表该绑定可能会被删除。此外,全局对象还会拥有一个名称为N的属性,并且该属性也会被赋予相应的值

这一点就解释了为什么我们在全局作用域下通过var声明的变量可以通过“global.变量名称”进行访问

- CreateGlobalFunctionBinding(N, V, D)

用于在object Environment Record组件中创建并初始化一个函数绑定,V为初始化绑定值(即绑定的函数)。该绑定是一个可变绑定,如果D为true,代表该绑定可能会被删除。此外,全局对象还会拥有一个名称为N的属性,这个属性的值即为函数V

Global Environment Record方法的执行逻辑

HasBinding ( N )

  • let: DclRec = env.[[DeclarativeRecord]]
  • if: DclRec.HasBinding(N), 返回 true
  • let: ObjRec = env.[[ObjectRecord]]
  • return ObjRec.HasBinding(N)

CreateMutableBinding ( N, D )

  • let: DclRec = env.[[DeclarativeRecord]]
  • if: DclRec.HasBinding(N)为true, 直接抛出TypeError
  • return DclRec.CreateMutableBinding(N, D).

CreateImmutableBinding ( N, S )

和CreateMutableBinding类似,只是最后一步调用的是DeclarativeRecord的CreateImmutableBinding方法

InitializeBinding ( N, V )

  • let: DclRec = env.[[DeclarativeRecord]]
  • if: DclRec.HasBinding(N)为true
    • return DclRec.InitializeBinding ( N, V )
  • Assert: 如果名称为N的绑定存在,那么必须是存在于object Environment Record中
  • let: ObjRec = env.[[ObjectRecord]]
  • return ObjRec.InitializeBinding(N, V)

SetMutableBinding ( N, V, S )

  • let: DclRec = env.[[DeclarativeRecord]]
  • if: DclRec.HasBinding(N)为true
    • return DclRec.SetMutableBinding ( N, V, S )
  • let: ObjRec = env.[[ObjectRecord]]
  • return ObjRec.SetMutableBinding ( N, V, S )

GetBindingValue ( N, S )

和上面的SetMutableBinding方法类似,只是调用的分别是DclRec和ObjRec的GetBindingValue方法

DeleteBinding ( N )

  • let: DclRec = env.[[DeclarativeRecord]]
  • if: DclRec.HasBinding(N)为true
    • return DclRec.DeleteBinding ( N )
  • let: ObjRec = env.[[ObjectRecord]]
  • let: globalObject = ObjRec.[[BindingObject]]
  • let: existingProp = HasOwnProperty(globalObject, N)
  • if: existingProp = true:
    • let:status = ObjRec.DeleteBinding(N)
    • if: status为true:
      • let: varNames = envRec.[[VarNames]]
      • 如果N是varNames的一个元素,则将其从varNames中删除
    • return status
  • return true

HasThisBinding ( )

-return true

global env是会提供this绑定的

HasSuperBinding ( )

  • return false

WithBaseObject ( )

  • return undefined 虽然global env中有一个object env组件,但是这个组件并不是通过with语句所创建的,因此总是返回undefined

GetThisBinding ( )

  • return env.[[GlobalThisValue]]

HasVarDeclaration ( N )

  • let: varDeclaredNames = envRec.[[VarNames]]
  • if: varDeclaredNames包含N,则return true
  • return false

HasLexicalDeclaration ( N )

  • let: DclRec = env.[[DeclarativeRecord]]
  • return DclRec.HasBinding(N)

HasRestrictedGlobalProperty ( N )

用于判断标识符N是不是global object的属性,这个属性不能被全局的词法声明所覆盖

  • let: ObjRec = env.[[ObjectRecord]]
  • let: globalObject = ObjRec.[[BindingObject]]
  • let: existingProp = globalObject.GetOwnProperty(N)
  • if: existingProp为undefined,return false
  • if: existingProp.[[Configurable]]为true,return false
  • return true

CanDeclareGlobalVar ( N )

  • let: ObjRec = env.[[ObjectRecord]]
  • let: globalObject = ObjRec.[[BindingObject]]
  • let: hasProperty = HasOwnProperty(globalObject, N)
  • if: hasProperty为true, return true
  • return IsExtensible(globalObject)

IsExtensible可以理解为Object.isExtensible方法

CanDeclareGlobalFunction ( N )

  • let: ObjRec = env.[[ObjectRecord]]
  • let: globalObject = ObjRec.[[BindingObject]]
  • let: existingProp = globalObject.GetOwnProperty(N)
  • if: existingProp为undefined,return IsExtensible(globalObject)
  • if: existingProp.[[Configurable]] 为true, return true
  • If: IsDataDescriptor(existingProp) 为 true,并且 existingProp has attribute values { [[Writable]]: true, [[Enumerable]]: true }, return true -return false

IsDataDescriptor(desc)的判定逻辑为:如果属性描述符desc既没有writable,也没有value属性,则为true;否则是false

CreateGlobalVarBinding ( N, D )

该方法是在global env的object Environment Record组件中创建一个可变绑定,并且在[[varNames]]记录中增加标识符N

  • let: ObjRec = env.[[ObjectRecord]]
  • let: globalObject = ObjRec.[[BindingObject]]
  • let: hasProperty = HasOwnProperty(globalObject, N)
  • let: extensible = IsExtensible(globalObject)
  • If: hasProperty为 false,并且 extensible为 true, 则:
    • 执行ObjRec.CreateMutableBinding(N, D)
    • 执行ObjRec.InitializeBinding(N, undefined)
  • let: varDeclaredNames = env.[[VarNames]]
  • If: varDeclaredNames中还没有包含 N, 则将N添加到varDeclaredNames记录中
  • return

CreateGlobalFunctionBinding ( N, V, D )

  • let: ObjRec = env.[[ObjectRecord]]
  • let: globalObject = ObjRec.[[BindingObject]]
  • let: existingProp = globalObject.GetOwnProperty(N)
  • if: existingProp为 undefined 或者 existingProp.[[Configurable]]为 true, 则:
    • let: 令desc为一个属性描述符 = PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }
  • 否则:
    • let: 令desc为一个属性描述符 = PropertyDescriptor { [[Value]]: V }
  • 执行: DefinePropertyOrThrow(globalObject, N, desc)
  • 执行: Set(globalObject, N, V, false)
  • let: varDeclaredNames = env.[[VarNames]]
  • If: varDeclaredNames中还没有包含 N, 则将N添加到varDeclaredNames记录中
  • return