Scope decides where a name is visible. JavaScript uses lexical scope: what you can see is fixed by where the code is written, not where it is called.
Kinds of scope
| Scope | Created by | Holds |
|---|---|---|
| Global | The program / script | Globals, globalThis properties |
| Module | Each ES module file | Top-level declarations of that module (not global!) |
| Function | Every function call | Parameters, var, inner declarations |
| Block | { }, if, for, try… | let, const, class (and function declarations in strict mode) |
The scope chain
When a name is used, the engine looks in the current scope, then the enclosing one, and so on out to global. The first match wins (shadowing); if none matches → ReferenceError.
const level = "global";
function outer() {
const level = "outer";
function inner() {
const extra = "inner-only";
console.log(level, extra); // finds outer's `level`
}
inner();
}
outer();
try {
console.log(extra);
} catch (e) {
console.log(e.name + ": " + e.message);
}outer inner-only
ReferenceError: extra is not definedLexical, not dynamic
const x = "lexical";
function readX() { return x; }
function caller() {
const x = "dynamic";
return readX(); // readX sees where it was DEFINED
}
console.log(caller());lexicalHoisting summary
| Declaration | Hoisted? | Value before the line runs |
|---|---|---|
function f() {} | Yes | The full function |
var x | Yes | undefined |
let / const | Yes (binding) | TDZ → ReferenceError |
class C {} | Yes (binding) | TDZ → ReferenceError |
const f = () => {} | As a const | TDZ |
import | Yes | Modules are linked before any code runs |
console.log(typeof hoisted, typeof notYet);
var notYet = function () {};
function hoisted() {}
var shadow = "global var";
function test() {
console.log(shadow); // local var is hoisted → undefined, not "global var"
var shadow = "local";
}
test();function undefined
undefinedAccidental globals
In sloppy mode, assigning to an undeclared name silently creates a global. In strict mode (and every ES module) it throws:
try {
undeclaredTotal = 10;
} catch (e) {
console.log(e.name + ": " + e.message);
}ReferenceError: undeclaredTotal is not definedWhat is hoisting?
What is lexical scoping?