The Scope Chain
A variable lookup walks outward through nested scopes until it finds a match or runs out of places to look.
Loading visual…
Definition
Every function and block creates its own scope, and scopes can nest inside one another: a global scope, an outer function's scope, and an inner function's scope, for example. When code inside the innermost scope reads a variable, the engine first checks that scope's own bindings. If the name is not there, it steps out to the next enclosing scope and checks again, continuing outward link by link. This chain of enclosing scopes is fixed at the time a function is defined, not at the time it is called, which is why JavaScript uses lexical (static) scoping. The lookup stops as soon as a match is found, so a variable in an inner scope shadows one of the same name further out. If the chain is walked all the way to the global scope and still no binding exists, the engine throws a `ReferenceError`, because there is nowhere else to look.
Examples
const x = "global";
function outer() {
const y = "outer";
function inner() {
console.log(x, y); // global outer
}
inner();
}
outer();inner() has no x or y of its own, so it walks the scope chain outward: first to outer's scope for y, then to the global scope for x.
function outer(x) {
function inner() { return x + 10; }
return inner();
}
outer(5); // 15inner reads x from the enclosing outer scope through the scope chain, even though x is not a parameter of inner itself.
Where you see this
- Understanding why a callback can read variables from the function that defined it.
- Debugging an unexpected ReferenceError, which usually means the lookup reached global with no match.
- Reasoning about variable shadowing when an inner scope reuses an outer variable's name.
- Knowing why module-level constants are visible inside every nested function without being imported.