Fundamentalsbeginner

Lexical Scope and Scope Chain

How JavaScript resolves variable names based on where code is physically written, not where it is called from.

Where a variable is visible is decided by where the code is written, not by where the function is called from.

It's like a family looking up a recipe: you check your own recipe box first, and if it's not there you ask your parents, then your grandparents — the search follows your family tree (where you came from), not who happens to be visiting your kitchen today.

Key Concepts

1
    const x = 'global';
2
    function outer() {
      const x = 'outer';
      inner();            // inner is defined elsewhere
    }
3
    function inner() {
      console.log(x);     // 'global'
    }
4
    outer();
5
inner is called from outer, and it does not see outer's x. It sees the scope it was defined in.
innerouterx
6
That is lexical scoping. Some languages do the opposite — dynamic scoping, where the caller's variables are visible — and this example is the difference.
7
The scope chain is the lookup path.
    function a() {
      const one = 1;
      function b() {
        const two = 2;
        function c() {
          console.log(one, two);   // found by walking up
        }
        c();
      }
      b();
    }
8
c looks in itself, then b, then a, then global, then throws ReferenceError. The chain is fixed when the function is written.
cbaReferenceError
9
Shadowing. An inner declaration with the same name is found first:
10
    const v = 'outer';
    function f() {
      const v = 'inner';
      console.log(v);     // 'inner'
    }
11
The outer v is not changed and is not reachable from inside — which is worth knowing when a variable seems not to update.
v
12
This is what makes closures work.
    function counter() {
      let n = 0;
      return () => ++n;
    }
    const next = counter();
    next();   // 1
    next();   // 2
13
counter has returned, and n still exists — because the returned function's scope chain includes counter's scope, and something still references that function.
countern
14
The scope is kept alive as long as the function referencing it is, which is also why a closure holding a large object keeps that object in memory.
15
The practical consequence. You can determine what a function can see by reading outwards from where it is written. You never need to know who calls it, which is what makes code readable at all.
16
What the interviewer is probing.1. "What determines what a function can see?" Probing: lexical versus dynamic scope. Stalls: "Where it is called." Moves up: where it is written — the scope chain is fixed at definition, which is what makes closures predictable.
17
2. "What is the scope chain?" Probing: resolution order. Stalls: "The call stack." *Moves up:* the nested environments searched outward from the function to the global scope until the name is found.
18
3. "Does this follow lexical scope?" Probing: the exception. Stalls: "Yes, like variables." Moves up: not in a regular function, where it depends on the call site; arrow functions are the exception and do inherit it lexically.
19
4. "Why can a function declared later still be referenced inside another function?" Probing: resolution timing. Stalls: "It cannot." Moves up: names resolve when the inner function runs, not when it is defined, so anything in scope by then is reachable.