In this article, we continue to learn Environment Records from the ECMAScript Specification. In the previous article, we looked at the different types of EnEnv and the abstract methods they must implement. In this article, we’ll take a look at the logic behind how each Env implements these abstract methods.

We introduce Declarative Environment Record, Function Environment Record and Module Environment Record

Declarative Environment Record

Declarative Environment Record relates closely to a code scope in ECMAScript. This code contains various grammars that define variables, constants, or functions: Variable, constant, let, class, module, import, function declarations, Declarative Env binds the identifiers declared in this scope and implements the following logic for the related abstract methods:

HasBinding(N)

Return true if Env has a binding to name N; Otherwise return false

CreateMutableBinding ( N, D )

  • Assert: Env does not have a binding for name N
  • Create an object named N for EnvvariableBind, and the state is marked uninitialized. If D is true, the binding may be deleted later
  • return

CreateImmutableBinding ( N, S )

  • Assert: Env does not have a binding for name N
  • Create an object named N for EnvimmutableBind, and the state is marked uninitialized. If S is true, the binding is treated as oneStrictly binding
  • return

InitializeBinding ( N, V )

  • Assert: Env already has an uninitialized binding named N
  • Set the binding value of name N to V
  • Record that the binding status of N changes to Initialized
  • return

SetMutableBinding ( N, V, S )

Try changing the value of binding N

  • There is no binding with name N in If Env
    • If S is true, a ReferenceError is raised
    • Else execute env.createmutableBinding (N, true)
    • Perform env. InitializeBinding (N, V)
    • return
  • If N is a strict binding, set S to true
  • If the state of the If N binding is uninitialized, a ReferenceError is raised
  • Else If N binding is mutable, set its binding value to V
  • Else
    • Assert: Attempts to change the value of an immutable binding
    • TypeError is raised if S is true
  • return

GetBindingValue ( N, S )

  • Assert: Env has a binding named N
  • If the binding of name N is uninitialized, a ReferenceError is raised
  • Returns the binding value of name N

DeleteBinding ( N )

  • Assert: Env has a binding named N
  • Return false if the binding with name N cannot be removed
  • Remove the binding with name N in env
  • return true

HasThisBinding ( )

  • return false

The normal Declarative Environment Record does not provide this, but the function Environment Record and Module Environment Record subtypes do have this bindings. This will be mentioned later

HasSuperBinding ( )

  • return false

WithBaseObject ( )

  • return undefined

Function Environment Record

Function Environment Record = Function Environment Record = Function Environment Record = Function Environment Record = Function Environment Record

  • Env also provides the this binding if the function is not an arrow function (This corresponds to the biggest feature of the arrow function: instead of having its own this value, its this value binds to the this value of the scope in which the function was created)
  • If it is not an arrow function and has an internal reference to super, then env also contains the state required for the super method call (this corresponds to the class inheritance syntax that references super, since ECMAScript classes are essentially function).

Function Environment Record has the following additional attributes compared to the parent type Declarative Environment Record:

The field names An optional value paraphrase
[[ThisValue]] Any The value of this when the function is called
[[ThisBindingStatus]] Lexical or initialized or uninitialized If it is lexical, it means that the function is an arrow function with no local this value
[[FunctionObject]] Object The function called when env is created
[[NewTarget]] The Object, or undefined If env is created by an internal [[Construct]] internal method call, then [[NewTarget]] points to the [[Construct]] method’s NewTarget argument (This means that the function is called with the new operator, in which case [[NewTarget]] points to the constructor.

Function Environment Record inherits all of the Declarative Environment Record methods, except that it overwrites the implementation of the HasThisBinding and HasSuperBinding methods. At the same time, it has the following unique methods:

Method names paraphrase
BindThisValue(V) Set the [[ThisValue]] property value mentioned above to V
GetThisBinding() Gets the this value of the binding
GetSuperBase() Returns the base value obtained by the super property

The specific implementation logic of the two covered methods and the three unique methods is as follows:

BindThisValue ( V )

  • Assert: Env’s [[ThisBindingStatus]] is not lexical
  • If: env [[ThisBindingStatus]] is initialized, raising ReferenceError
  • Set env’s [[ThisValue]] to V
  • Set env’s [[ThisBindingStatus]] to initialized
  • return V

HasThisBinding ( )

  • If env’s [[ThisBindingStatus]] is lexical, return false; Otherwise return true

HasSuperBinding ( )

  • If: env’s [[ThisBindingStatus]] is lexical, and returns false
  • If: env [[FunctionObject]].[[HomeObject]] is undefined, return false; Otherwise return true

The [[HomeObject]] property is one of the built-in properties of ECMAScript function objects. It has a value of type Object and means: If the function uses super, this is the object whose [[GetPrototypeOf]] provides the object where super property lookups begin.

Where [[GetPrototypeOf]] is equivalent to object.getProtoTypeof

The implicit prototype object of [[HomeObject]] is the initial object to look up properties or methods when super is used in a function for retrieving properties or calling methods. This is mainly used for class inheritance syntax, because in the inheritance syntax, the use of super is very diverse, you can refer to teacher Ruan’s article es6.ruanyifeng.com/#docs/class…

GetThisBinding ( )

  • Assert: Env’s [[ThisBindingStatus]] is not lexical
  • If: env [[ThisBindingStatus]] is uninitialized and raises ReferenceError
  • return evn.[[ThisValue]]

GetSuperBase ( )

  • let home = env.[[FunctionObject]].[[HomeObject]]
  • If: home is undefined, return undefined
  • Assert: Typeof Home is an Object
  • An implicit prototype object for return Home

Module Environment Record

Module Environment Record is another subtype of Declarative Environment Record, which represents the outer scope of an ES Module. Es6.ruanyifeng.com/#docs/modul…).

In addition to the normal mutable and immutable bindings, Module Environment Record also provides immutable import bindings that enable the current Module to acquire bindings from other enVs. (In short, the Module scope can import external bindings through import syntax, And these bindings are immutable.)

Function Environment Record inherits all the methods of Declarative Environment Record, But the GetBindingValue, DeleteBinding, HasThisBinding and GetThisBinding methods are overridden, and CreateImportBinding and GetThisBinding methods are added.

The meanings of the two new methods are shown in the following table:

Method names paraphrase
CreateImportBinding(N, M, N2) Create an indirect immutable binding where N is the binding name, M is a Module Record, and N2 is a binding that exists in M (Module Record is also a data structure within the standard, we can indirectly understand here is a Module can be)
GetThisBinding() Get env’s this binding

The execution logic for the four overridden and unique two methods is as follows:

CreateImportBinding ( N, M, N2 )

Create an immutable indirect binding for env, which is then fetched via a pointer to the N2 binding in M.

An important feature of the ES Module is that the ES Module outputs a reference to a value, because when the value is fetched in the imported Module, it is actually evaluated in the target Module

  • Assert: env Does not currently have a binding named N
  • Assert: M is a Module Record
  • Assert: there is a direct binding to N2 in M
  • Create an indirect binding for env named N and reference the N2 binding in M as its target binding, recording the binding status as initialized
  • return

GetBindingValue ( N, S )

  • Assert: S is true. The ES Module automatically turns on strict mode.
  • Assert: env Has a binding named N
  • If: The binding is an indirect binding
    • Get the indirectly bound M and N2
    • Get env in M as targetEnv
    • If targetEnv is undefined, a ReferenceError is raised
    • Return targetEnv. GetBindingValue (N2, true)
  • Env: the binding state of name N is uninitialized, raising ReferenceError
  • Binding of the name N in return env

DeleteBinding ( N )

The DeleteBinding method in the Module Environment Record is not used

HasThisBinding ( )

Returns true

GetThisBinding ( )

Returns the undefined

From the behavior of the above methods, we can see that the biggest difference of Module Environment Record is the ability to import external binding values, which can be achieved through import statements. Also, you can see that this is always undefined in the outer scope of the ES Module

Object Environment Record and Global Environment Record will continue in the next article