Reading the ECMA-262: How Declarations at Global Level Work

Published on

There are four kinds of context in which variable or function declarations would occur: Global, module, function, and block. This article focuses on the global context.

Steps in Specification of Declarations in Global Context

A JavaScript engine instance is responsible for the code execution. The engine will parse the code to identify what kinds of statements and expressions there are.

But before actually executing the code line by line, the engine needs to set up the environment that is essential for executing.

By design, all JavaScript code should run in its corresponding execution context in a single execution stack. So, the engine needs to create the first execution context and push it to the stack.

Here are the steps in the specification of how a piece of global code is processed before execution:

  1. The engine creates and initializes a Realm for the execution context: (InitializeHostDefinedRealm)
    1. Create [[Intrinsics]] = new Record
      1. Set properties of realmRec.[[Intrinsics]] for engine internal use. (%Object%, %Function%, %Array%, %String%, %Number%, %Map%, %Set%, %Date%…)
    2. Create [[GlobalObject]] = undefined
    3. Create [[GlobalEnv]] = undefiend
      1. Create an empty global object and set it to realmRec.[[GlobalObject]]
      2. globalEnv ← a new Lexical Environment with:
        1. outer environment ← null
        2. EnvironmentRecord ← Create a global environment record with:
          1. [[ObjectRecord]] ← Create a object Environment Record with global as its binging object
          2. [[GlobalThisValue]] ← the global object
          3. [[DeclarativeRecord]] ← Create a declarative Environment Record without bindings
          4. [[VarNames]] ← Create a new empty List
    4. Set all properties (global object, functions…) to realmRec.[[GlobalObject]]
  2. The engine sets the first execution context (ScriptEvaluation)
    1. scriptContext ← a new ECMAScript code execution context with:
      1. Functionnull
      2. RealmscriptRecord.[[Realm]]
      3. ScriptOrModulescriptRecord
      4. VariableEnvironmentscriptRecord.[[Realm]].[[GlobalEnv]]
      5. LexicalEnvironmentscriptRecord.[[Realm]].[[GlobalEnv]]
  3. The engine instantiates the global declarations (GlobalDeclarationInstantiation)
    1. envRec = environment record of scriptRecord.[[Realm]].[[GlobalEnv]]
    2. Get LexicallyDeclaredNames and VarDeclaredNames (function + var) from script
    3. Conflict Checks
      1. for Lexical Declarations
      2. for Variable Declarations (only conflict with lexical not allowed)
    4. Split VarScopedDeclarations
      1. Pick Function Declarations: If there’re duplicated functions with same name, only take the last
      2. Pick Variable Declarations: If there’re duplicated variables with same name, only take the first
    5. Instantiate Lexical Declarations
      1. const → envRec.CreateImmutableBinding(name), let → envRec.CreateMutableBinding(name)
        • Note: Lexically declared names are only instantiated here but not initialized
        • [[DeclarativeRecord]].CreateMutableBinding(name) and [[DeclarativeRecord]].CreateImmutableBinding(name)
    6. Instantiate and initialize Function Declarations
      • envRec.CreateGlobalFunctionBinding(name, InstantiateFunctionObject, false)
        • Create and initialize a global function binding in the [[ObjectRecord]] component of a global Environment Record.
    7. Instantiate and initialize Var Declarations with undefined
      • envRec.CreateGlobalVarBinding(name, false)
        • Used to create and initialize to undefined a global var binding in the [[ObjectRecord]] component of a global Environment Record.
  4. The engine executes the code
    1. Evaluate the assignments of values to var, let, and const variables when encountering these statements.

We can see the behavior known as “hoisting” in these steps:

  1. Lexical declarations are only instantiated here but not initialized.
  2. Function declarations are instantiated and initialized with the function object as its value
  3. Var declarations are instantiated and initialized with undefined as its value

Recap

Here is the outline of the steps to prepare a new execution context for a global code execution:

  1. Create and initialize a Realm record with its [[Intrinsics]], [[GlobalObject]], and [[GlobalEnv]]. The environment record of [[GlobalEnv]] has some important internal slots:
    1. [[ObjectRecord]]
    2. [[DeclarativeRecord]]
    3. [[GlobalThisValue]] ← point to the [[GlobalObject]] object
  2. Create first ECMAScript code execution context with Realm, and its [[GlobalEnv]] for VariableEnvironment and LexicalEnvironment
  3. Instantiate the declarations in the scriptRecord to the environment record of scriptRecord.[[Realm]].[[GlobalEnv]]
    1. lexical → bond to [[DeclarativeRecord]], not initialized
    2. var → bond to [[ObjectRecord]], initialized with undefined as its value
    3. function → bond to [[ObjectRecord]], initialized with the function object as its value
  4. Finally the engine can start executing the code and evaluating the assignments.