skip to content

Scope and Closures

How JavaScript decides which variable a name refers to, and how a function keeps hold of the variables it was born next to. Declarations and hoisting, the execution model, closures, and the this binding are the classic JavaScript screen — most rounds open here.

part ofJavaScriptoverview, primer and where to startread it →
on this pageshow

explore

questions

page 1 of 2

Given `function makeCounter() { let n = 0; return () => ++n; }` and `const a = makeCounter(), b = makeCounter();`, what does `a(); a(); console.log(a(), b());` print, and why do the two counters not interfere?

level: juniorimportance: must knowfreq 70%

answer

  1. one environment per call
  2. state lives in the call, not the function
  3. returned function keeps it alive
  4. two calls, two n bindings
  5. private because nothing else can name it

basics

~10 s

It prints 3 1. Every call to makeCounter creates a new environment with its own n, so the two returned functions close over two different bindings and count independently.

solid answer

~40 s

It logs `3 1`. Each invocation of `makeCounter` creates a fresh execution environment containing its own `n` binding; the arrow function returned from that call captures *that* environment. So `a` and `b` were produced by two separate calls and therefore increment two separate `n` bindings — `a` has been called three times, `b` once. The environment is not destroyed when `makeCounter` returns, because the returned function still references it; that is exactly what makes `n` persistent private state. Nothing outside the returned function can read or write `n`, since the only reference to that environment is held by the closure itself. Independence comes from the *call*, not from the function: two closures made in the *same* call would share one `n`.

code

javascript · 9 lines
javascript
function makeCounter() {
  let n = 0;
  return () => ++n;
}
const a = makeCounter();
const b = makeCounter();
a();
a();
console.log(a(), b()); // 3 1

go deeper

for a junior

Know the output cold and be able to say that each call to the factory makes its own n. Practise writing the counter from memory in under thirty seconds.

for a middle

Explain the environment record: a fresh one per invocation, captured by the function created inside it, kept alive by that reference after the call returns.

for a senior

Use it to reason about real factories — connection pools, rate limiters, id generators — and be ready to say what accidentally moves state from per-instance to shared, and how you would catch that in review.

for a principal

Frame the choice of where state lives as an API decision: per-call closure state versus module-level state changes testability, isolation between callers, and what happens when the same module is loaded twice.

## The output `a()` returns 1, then 2, then 3; `b()` returns 1. The logged line is `3 1`. ## One environment per invocation Calling a function creates a new execution context, and with it a fresh environment record holding bindings for that call's parameters and local declarations. `let n = 0` therefore creates a *new* `n` on every call to `makeCounter`. The arrow function created during that call stores a reference to that specific environment. So `makeCounter()` called twice produces two function objects pointing at two different environments, each with its own `n`. Incrementing one cannot be observed through the other. This is the single most important corollary of closure capture: independence is a property of the enclosing **call**, not of the enclosing **function**. ## Why n survives after the function returns A naive model of scope says local variables disappear when a function returns. That model is about stack frames, and it does not describe JavaScript. The environment record is an ordinary reachable object; as long as something references it — here, the returned arrow function — it stays alive and its bindings keep their values. When the last closure over it is dropped, the whole environment becomes unreachable along with it. That is why `n` behaves like an instance field: it is initialised once per construction (per call), it persists between uses, and it is genuinely private. ## Privacy is a consequence, not an extra feature There is no syntax here declaring `n` private. It is unreachable from outside simply because there is no expression anywhere in the program that names it: identifier resolution walks the *lexical* chain, and no code outside `makeCounter`'s body sits inside that chain. The only way to touch `n` is through a function that was written inside `makeCounter`. If you want a reader as well as an incrementer, you must return both from the same call. ## The contrast that makes the point Move the state out of the function and the independence vanishes: ``` let shared = 0; function makeBadCounter() { return () => ++shared; } const x = makeBadCounter(); const y = makeBadCounter(); x(); // 1 y(); // 2 — same binding ``` Here both closures resolve `shared` in the same outer environment, which was created once. Two calls to `makeBadCounter` do create two environments, but neither of them contains `shared`, so the lookup continues outward to the one binding that exists. The lesson is that what matters is *which environment holds the binding you are reading*, not how many closures exist. ## Naming the pattern This is the counter/factory idiom interviewers use to check that the candidate has the environment model rather than a memorised slogan. Related shapes you can mention: a `once` wrapper holding a `called` flag, a memoiser holding its cache, an ID generator holding a seed. All of them rely on the same two facts — per-call environment, and survival of that environment for as long as a closure references it. ## Common wrong answers Saying it prints `3 3` treats `n` as if it belonged to the function rather than the call. Saying it prints `1 1` assumes each invocation of the returned closure re-runs `let n = 0`, which it does not — the returned function's body is only `++n`. Saying `n` is garbage collected after `makeCounter` returns confuses stack frames with reachable environments. ## How to answer out loud Give the output first, then the one-sentence reason: a fresh environment per call, captured by the function created during that call. Add that this is why the pattern gives private, persistent state — and, if you want to show depth, add the contrast case where the state is declared outside the factory and the counters collide.

  • Does the environment holding n disappear when makeCounter returns?
    No. The returned function references that environment, so it stays reachable and `n` keeps its value between calls. Only when every closure over it is dropped does the environment become unreachable. The stack frame is gone; the environment record is a separate, reachable thing.
  • How would you add a way to read the count without letting callers set it?
    Return both functions from the same call, for example `return { inc: () => ++n, read: () => n };`. Both close over the same `n` because they were created in the same invocation. Callers get a read path and an increment path, and still no way to assign to `n` directly.
  • If I move `let n = 0` outside makeCounter, what changes?
    All counters then share one binding, because their lookups walk past the per-call environment — which no longer declares `n` — to the single outer one. `x()` then `y()` returns 1 then 2. It also stops being private: any code in that outer scope can read or write `n`.

saying these in an interview costs you the question

  • Says it prints 3 3 because n belongs to the function
  • Says it prints 1 1 because n resets on each call
  • Claims n is destroyed when makeCounter returns
  • Thinks the two counters share state because the code is identical
  • Calls n a global variable

context

open as a page

In JavaScript, `for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); }` logs 3, 3, 3. Explain why, and give the smallest change that makes it log 0, 1, 2.

level: juniorimportance: must knowfreq 85%

basics

~20 s

var creates one function-scoped i shared by the whole loop, so all three deferred callbacks read the same binding, which already holds 3 by the time the timers fire. Declaring the counter with let gives each iteration its own i, logging 0, 1, 2.

open as a page

In JavaScript, why is a function's local variable not freed when that function returns, if an inner function it returned still refers to the variable?

level: juniorimportance: must knowfreq 58%

basics

~20 s

A function object stores a hidden pointer to the scope it was created in, so returning it keeps that scope reachable. Reachable memory is never collected, so the variable lives as long as the returned function does.

open as a page

In JavaScript, why does `function () { console.log('hi'); }();` throw a SyntaxError while `(function () { console.log('hi'); })();` runs, and what other forms make an immediately invoked function expression work?

level: juniorimportance: must knowfreq 62%

basics

~20 s

A statement that begins with the keyword function is parsed as a function declaration, which needs a name and cannot be invoked in place. Wrapping it in parentheses forces the parser to read it as an expression, and expressions are callable.

open as a page

In JavaScript, why can you call a function written as `function greet() {}` from a line above its definition, while calling a function stored by `var sayHi = function () {}` above that assignment throws a TypeError?

level: juniorimportance: must knowfreq 80%

basics

~20 s

A function declaration is created whole, name and body together, when the scope is entered, so it is callable anywhere in that scope. A function expression only becomes a function when its assignment line runs; until then the variable holds undefined.

open as a page

What does this JavaScript script print, and why? `console.log(a); var a = 1; console.log(b); let b = 2;`

level: juniorimportance: must knowfreq 78%

basics

~20 s

Logs undefined, then throws a ReferenceError. Both bindings already exist when the script starts, but a var binding is pre-initialized to undefined, while a let binding is left uninitialized until its own declaration line runs.

open as a page

In JavaScript, why does a variable declared with var inside an if block stay visible after that block ends, while the same variable declared with let does not?

level: juniorimportance: must knowfreq 85%

basics

~20 s

var binds to the nearest enclosing function (or the script itself), so blocks are invisible to it. let and const bind to the nearest pair of braces, so their names stop existing when that block ends.

open as a page

In JavaScript, what happens at runtime when code reads an identifier that no environment in the scope chain declares — for example `console.log(total)` where `total` was never declared — and how is that different from reading a variable that currently holds `undefined`?

level: juniorimportance: must knowfreq 62%

basics

~10 s

Reading an undeclared identifier throws a ReferenceError. The engine walks the scope chain outward to the global environment, finds no binding with that name, and aborts evaluation instead of quietly returning undefined.

open as a page

What does the "use strict" directive do in JavaScript, and where exactly must it appear in a script or function body to take effect?

level: juniorimportance: must knowfreq 62%

basics

~20 s

"use strict" switches a whole script or a single function body into strict mode, a stricter dialect where silent failures throw and undeclared assignments are errors. It works only as the very first statement of that script or function body.

open as a page

Given `const counter = { count: 0, inc: () => { this.count++; } }`, why does calling `counter.inc()` fail to increment `counter.count`, and what do you change?

level: juniorimportance: must knowfreq 80%

basics

~20 s

Arrow functions have no this of their own. The arrow reads this from the scope where the object literal was written, not from the receiver, so counter.inc() touches that outer this instead of counter. Use a normal method.

open as a page

In JavaScript, what is the difference between Function.prototype.call and Function.prototype.apply, and how does Function.prototype.bind differ from both?

level: juniorimportance: must knowfreq 80%

basics

~20 s

call and apply both invoke the function immediately with an explicit this; call takes the arguments individually, apply takes them as one array-like. bind invokes nothing — it returns a new function whose this is permanently fixed.

open as a page

In JavaScript, counter.increment() works, but `const inc = counter.increment; inc();` throws because this is undefined. Explain why pulling a method out of its object loses the receiver.

level: juniorimportance: must knowfreq 78%

basics

~20 s

JavaScript decides this at call time from whatever sits to the left of the dot, not where the function was written. Extracting a method copies only the function value, so a later plain call has no receiver and this falls back to undefined or globalThis.

open as a page

What does `this` refer to inside a regular JavaScript function that is invoked on its own, as a bare `doThing()`, and how does strict mode change the answer?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A bare call supplies no receiver, so default binding applies: this is globalThis in sloppy mode and undefined in strict mode. Class bodies and ES modules are always strict, so undefined is the modern norm.

open as a page

In JavaScript, when a function closes over an outer variable, does it capture that variable's value at the moment the function was created, or the variable binding itself? Show the difference with a variable that is reassigned after the function is created.

level: middleimportance: must knowfreq 74%

basics

~10 s

JavaScript closures capture the binding, not a copy of the value. The function reads the variable when it runs, so any reassignment made after the function was created is visible to the closure.

open as a page

A `for (let i = 0; i < 3; i++)` loop declares the variable only once in its head, yet each iteration behaves as if it has its own `i`. How does JavaScript produce that, and which iteration's binding does the `i++` update?

level: middleimportance: must knowfreq 55%

basics

~20 s

The loop creates a new environment for every iteration and copies the loop variable's current value into it. The increment then runs against the new iteration's copy, so the previous iteration's binding is never touched again and closures over it keep that iteration's value.

open as a page

Using only closures — no class syntax and no separate module file — how do you build a single JavaScript object that exposes a public API while keeping its internal state genuinely unreachable from outside, and what makes it unreachable?

level: middleimportance: must knowfreq 58%

basics

~20 s

Run a function immediately and return only the object you want callers to have. Everything declared inside the function stays in that function's scope, which no outside expression can name, so the returned methods are the only path to the state. That is the module pattern.

open as a page

What exactly is the temporal dead zone in JavaScript, and when does a let or const binding enter and leave it?

level: middleimportance: must knowfreq 62%

basics

~20 s

The temporal dead zone is the window in which a let, const, or class binding exists but is uninitialized, so any access throws a ReferenceError. It begins when the scope is entered and ends when that binding's own declaration is evaluated.

open as a page

In JavaScript, what exactly does const protect — and why can you still call push() on an array that was declared with const?

level: middleimportance: must knowfreq 80%

basics

~10 s

const makes the binding unchangeable, not the value. The name can never be pointed at a different value, but if that value is an object or array its contents stay fully mutable.

open as a page

In JavaScript, a function `show` is declared at the top level of a file and logs a variable `value`; a separate function `run` declares its own local `const value = 'inner'` and then calls `show()`. Which binding does `show` resolve, and what rule decides it?

level: middleimportance: must knowfreq 66%

basics

~20 s

It resolves the top-level value, not the caller's. JavaScript scoping is lexical: a function's outer environment link is fixed where the function is written in the source, so the caller's local bindings are never on its scope chain.

open as a page

In a strict-mode script, what is `this` inside a function invoked as a plain `f()`, and why does the identical code see the global object when the script is not strict?

level: middleimportance: must knowfreq 70%

basics

~20 s

In strict mode this is undefined in a plain f() call. Sloppy mode substitutes the global object whenever the incoming this value is undefined or null, and wraps a primitive this value in an object — strict mode passes it through untouched.

open as a page

In JavaScript, if you take a function returned by Function.prototype.bind and then call it with call, apply, or bind again, which this value wins?

level: middleimportance: must knowfreq 62%

basics

~20 s

The first bind wins. bind produces a hard-bound function whose receiver is permanent, so a later call, apply, or bind cannot override it — the extra thisArg is silently ignored, though extra arguments are still passed through.

open as a page

You need to hand an object's method to a callback API such as setTimeout without losing its receiver. Compare binding the method once in the constructor with wrapping the call in an arrow function at the call site.

level: middleimportance: must knowfreq 62%

basics

~20 s

Binding in the constructor stores an own, permanently-bound copy on the instance, giving one stable function you can pass anywhere. An arrow wrapper at the call site leaves the method untouched and simply calls it with the dot, creating a fresh function each time. Prefer the wrapper unless you need a stable identity.

open as a page

In JavaScript, how does the engine decide what `this` refers to inside a regular (non-arrow) function, and in what order do the binding rules apply when more than one could?

level: middleimportance: must knowfreq 78%

basics

~10 s

JavaScript resolves this at the call site, using four rules in priority order: new binding, explicit call/apply/bind, the object before the dot, then the default — globalThis in sloppy mode, undefined in strict mode.

open as a page

A factory returns `{ inc, value, get }` where `value` is set to the local `count` and `get` returns `count`. After calling `inc()` twice, `get()` returns 2 but `value` is still 0. Explain why, and how you would fix it.

level: middleimportance: should knowfreq 48%

basics

~20 s

The property value was assigned the number that count held while the object literal was being built, so it is a one-time copy. get closes over the count binding and re-reads it on every call, which is why only it stays current.

open as a page

`for (var v = 0; v < 3; v++) setTimeout(() => console.log(v))` logs 3, 3, 3, but `[0, 1, 2].forEach(v => setTimeout(() => console.log(v)))` logs 0, 1, 2 — and neither uses `let`. What accounts for the difference?

level: middleimportance: should knowfreq 40%

basics

~20 s

forEach calls its callback once per element, and every call creates a new environment holding its own parameter binding for that element. The counting loop has a single shared var binding instead. Callback-based iteration gets per-iteration bindings for free, whatever declaration keyword you use.

open as a page

Before `let` existed, how did you make `for (var i = 0; i < 3; i++) setTimeout(function () { console.log(i); }, 0);` log 0, 1, 2 without changing `var`? Show the wrapper and say what actually makes it work.

level: middleimportance: should knowfreq 42%

basics

~20 s

Wrap the body in an immediately-invoked function that takes the counter as a parameter. Argument passing copies the value into a fresh parameter binding per call, so the scheduled callback closes over that copy rather than the one shared var. Function.prototype.bind achieves the same by fixing the argument.

open as a page

In JavaScript, a function loads a large object and returns a small callback that will be stored for the lifetime of the process. How do you stop that callback from retaining the large object?

level: middleimportance: should knowfreq 42%

basics

~20 s

Capture only the small value the callback needs, computing it before the callback is created, so the large object is never part of the callback's reachable graph. If it must be referenced, release the binding by assigning null once you are done with it.

open as a page

Several classic (non-module) JavaScript files loaded with plain script tags need to share one global namespace object. Explain the `var App = App || {};` plus IIFE-augmentation idiom, why it works regardless of file order, and why swapping `var` for `let` breaks it.

level: middleimportance: should knowfreq 30%

basics

~20 s

Each file writes var App = App || {}; then an IIFE that hangs its own members on App. Because var may be redeclared and is hoisted-initialized to undefined, whichever file loads first creates the object and the rest reuse it. let throws instead of reusing.

open as a page

In JavaScript, given `const f = function fact(n) { return n < 2 ? 1 : n * fact(n - 1); };`, where is the name `fact` visible, and what do you gain by naming a function expression instead of leaving it anonymous?

level: middleimportance: should knowfreq 35%

basics

~20 s

The name of a named function expression is visible only inside that function's own body, not in the surrounding scope. It gives the function a reliable way to call itself and puts a real name in stack traces and debugger frames.

open as a page

Before ES2015, `typeof x` was a safe way to probe a variable that might not exist. What changed once let and const arrived, and when does typeof now throw?

level: middleimportance: should knowfreq 44%

basics

~20 s

typeof is still safe for a completely undeclared name — it returns the string "undefined". But if the name is declared with let, const, or class in an enclosing scope and is still in its temporal dead zone, typeof throws a ReferenceError like any other access.

open as a page

showing 1–30 of 56