JavaScript Interview Prep
Scope & Closures

Lexical Scope

Where You Write a Function Decides What It Can See

LinkedIn Hook

JavaScript picks which variables a function can see the moment you write it — not when you call it.

That one rule is called lexical scope, and it quietly controls almost every "weird" behavior in JavaScript. Why does a function defined in the global scope still read the global variable — even when called from deep inside another function with the same name? Why can a child function see its parent's variables, but a parent can never peek inside its child? Why is scope "decided at compile time" in a language that's famously dynamic?

If you've ever been burned by a function that returned "the wrong value" because you assumed it would look up variables from the caller — you were fighting lexical scope.

In this lesson you'll learn what lexical (static) scope actually is, why it's the opposite of dynamic scope, and how nested scopes form a strict one-directional lookup: inner can see outer, never the other way around.

Read the full lesson -> [link]

#JavaScript #InterviewPrep #LexicalScope #Scope #Frontend #CodingInterview #WebDevelopment


Lexical Scope thumbnail


What You'll Learn

  • What lexical (static) scope is and how it differs from dynamic scope
  • Why scope is decided at write time, not run time
  • How nested scopes form a one-directional visibility chain (inner sees outer, never reverse)

The Building-Floors Model

Think of lexical scope like a building with floors. If you're on the 3rd floor and need a tool, you first check your own floor. If it's not there, you go down to the 2nd floor, then the 1st floor, then the lobby (global). You never go up. And the building's layout was decided when it was built (when the code was written), not when someone walks through it (when the code runs).

That's lexical scope — JavaScript determines which variables a function can access based on where the function is written in the source code, not where or how it's called.

Static vs Dynamic Scope

JavaScript uses lexical (static) scope. This means scope is determined at write time (when the code is authored), not at run time (when the function is invoked). Some languages like Bash use dynamic scope, but JavaScript never does.

let language = "JavaScript";

function printLanguage() {
  console.log(language);
}

function wrapper() {
  let language = "Python";
  printLanguage(); // What does this print?
}

wrapper();
// Output: "JavaScript" (NOT "Python"!)

Why? Because printLanguage was defined in the global scope. Its lexical environment is the global scope, so it looks up language from there — regardless of where it's called from. If JavaScript used dynamic scope, this would print "Python".

Nested Lexical Scope

function grandfather() {
  let familyName = "Hasan";

  function father() {
    let fatherName = "Karim";

    function child() {
      let childName = "Rakib";
      // child can access ALL variables from outer scopes
      console.log(familyName); // "Hasan"     -- from grandfather
      console.log(fatherName); // "Karim"     -- from father
      console.log(childName);  // "Rakib"     -- own scope
    }

    child();
    // console.log(childName); // ReferenceError -- can't look inward
  }

  father();
  // console.log(fatherName); // ReferenceError -- can't look inward
}

grandfather();

The key rule: inner scopes can access outer scopes, but outer scopes cannot access inner scopes. This is one-directional, always outward.

Lexical Scope Is Decided at Compile Time

function createGreeter() {
  let greeting = "Hello";

  return function (name) {
    // This function's lexical scope includes `greeting`
    // This was decided when the code was WRITTEN
    console.log(`${greeting}, ${name}!`);
  };
}

const greet = createGreeter();
greet("World"); // "Hello, World!"
// Even though createGreeter() has returned,
// the inner function still has access to `greeting`

Lexical Scope visual 1


Common Mistakes

  • Assuming a function inherits variables from its caller's scope — it doesn't. JavaScript uses lexical (static) scope, so a function only sees variables from where it was defined.
  • Expecting an outer scope to see variables declared in an inner function. The visibility is one-directional: outward only.
  • Confusing "lexical scope" with "closures." Lexical scope is the rule (where the function was written). A closure is the consequence (the function keeps access to that lexical environment even after the outer function returns).

Interview Questions

Q: What is lexical scope in JavaScript?

Lexical scope (also called static scope) means a function's accessible variables are determined by where the function is written in the source code, not where or how it's called. The scope is fixed at author/compile time and travels with the function for its entire life.

Q: Does JavaScript use static or dynamic scope? What's the difference?

JavaScript uses static (lexical) scope. Static scope means variable access is determined by where a function is written in the source code. Dynamic scope would determine variable access by the call stack at runtime. In the example above, printLanguage() accesses the global language ("JavaScript"), not the local one in wrapper() ("Python"), because its scope was fixed when it was defined.

Q: Can an outer function access variables declared inside an inner function?

No. Scope visibility is one-directional — inner functions can read variables from any enclosing outer scope, but outer scopes cannot reach into an inner function's local variables. Trying to do so throws a ReferenceError.

Q: Where is scope determined — at write time or run time?

At write time (author/compile time). The lexical environment of a function is fixed the moment the source code is parsed. Calling the function from a different place, or even from a different file, cannot change which outer variables it sees.


Quick Reference — Cheat Sheet

LEXICAL SCOPE -- QUICK MAP

Rule:
  Scope is decided at WRITE time, not RUN time.
  A function sees variables from where it was DEFINED,
  NOT from where it was CALLED.

Direction of visibility:
  inner  -> outer   OK   (can look outward)
  outer  -> inner   NO   (ReferenceError)

Static vs Dynamic:
  JavaScript           -> static / lexical (always)
  Bash / some LISPs    -> dynamic (caller's scope)

Mental model:
  Building with floors. You can walk DOWN to the lobby
  (global), never UP into a floor that nested inside you.

Previous: Memory Heap & Garbage Collection Next: Scope Chain -> The Variable Lookup Ladder


This is Lesson 2.1 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.

On this page