AngryJohn
AngryJohn

Reputation: 643

Can't find where ECMAScript mentioned the about scope chain

Hey so i've been reading lately the ecmascript spec and i've encountered a talk about LexicalEnvironment and VariableEnvironment

like

  1. Let oldEnv be the running execution context's LexicalEnvironment.
  2. Let blockEnv be NewDeclarativeEnvironment(oldEnv).
  3. Perform BlockDeclarationInstantiation(StatementList, blockEnv).
  4. Set the running execution context's LexicalEnvironment to blockEnv.
  5. Let blockValue be the result of evaluating StatementList.
  6. Set the running execution context's LexicalEnvironment to oldEnv.
  7. Return blockValue.

right the LexicalEnvironment now points to the blockEnv which is a declarative Environment Record

but it doesn't says at what place we are searching for variables.

based on logic i assume it's searching inside the LexicalEnvironment.

what i'm asking is where can i find the rule that's says the place where we are looking for variables and functions.

Upvotes: 2

Views: 159

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074809

The scope chain is implemented via the [[OuterEnv]] property of environment objects.

Every Environment Record has an [[OuterEnv]] field, which is either null or a reference to an outer Environment Record. This is used to model the logical nesting of Environment Record values. The outer reference of an (inner) Environment Record is a reference to the Environment Record that logically surrounds the inner Environment Record.

Basically, when looking up the binding for an identifier, the current environment object is used, and if no matching binding is found, the one linked via [[OuterEnv]] is used, and so on.

You can see that in ResolveBinding which uses GetIdentifierReference. If GetIdentiferReference on doesn't find the binding on the environment object it's called with, it calls itself recursively with that environment's [[OuterEnv]]:

  • If env is the value null, then
    • Return the Reference Record { [[Base]]: unresolvable, [[ReferencedName]]: name, [[Strict]]: strict, [[ThisValue]]: empty }.
  • Let exists be ? env.HasBinding(name).
  • If exists is true, then
    • Return the Reference Record { [[Base]]: env, [[ReferencedName]]: name, [[Strict]]: strict, [[ThisValue]]: empty }.
  • Else,
    • Let outer be env.[[OuterEnv]].
    • Return ? GetIdentifierReference(outer, name, strict).

In a comment you asked why we need VariableEnvironment. We have both VariableEnvironment and LexicalEnvironment (and GlobalEnvironment, for that matter), which are all environment record objects (1, 2). We need variable environment vs. lexical environment in order to handle var-scoped declarations differently from lexically-scoped declarations (let, const, etc.). For example:

function example() {
    // Here, the LexicalEnvironment and the VariableEnvironment
    // are the same environment record; let's call it "outer"
    var a = 1; // Created in "outer"
    {
        // Here, there's a new environment set as the LexicalEnvironment,
        // using "outer" as its [[OuterEnv]. Let's call it "inner". See
        // https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
        var b = 2; // Created in "outer"
        let c = 3; // Created in "inner"
        console.log(typeof a); // "number", found in "outer"
        console.log(typeof b); // "number", found in "outer"
        console.log(typeof c); // "number", found in "inner"
    }
    console.log(typeof a); // "number", found in "outer"
    console.log(typeof b); // "number", found in "outer"
    console.log(typeof c); // "undefined", not found at all
}
example();

Upvotes: 2

Related Questions