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 2 of 2

In JavaScript, which redeclarations of the same name does var allow that let and const reject, and when does declaring a name in a nested block become a SyntaxError rather than legal shadowing?

level: middleimportance: should knowfreq 55%

basics

~20 s

var silently allows the same name twice in one scope; let and const make it a SyntaxError. Shadowing the same name in a nested block is always legal for let and const, but a var inside the block collides with an outer let because var is hoisted past the braces.

open as a page

In a non-strict JavaScript script, what happens when a function assigns to a name that is not declared anywhere in the scope chain — for example `function init() { cache = {}; }` — and why is it a bug worth catching?

level: middleimportance: should knowfreq 48%

basics

~20 s

The assignment walks the scope chain, finds no binding, and in non-strict script code creates a property on the global object instead of throwing. That accidental global outlives the call and is shared by every other script on the page or process.

open as a page

Why does assigning to a property of an object that was passed to Object.freeze() do nothing at all in sloppy mode, yet throw a TypeError under "use strict"?

level: middleimportance: should knowfreq 48%

basics

~10 s

The assignment fails in both dialects. Sloppy mode discards the failure and continues; strict mode reports it as a TypeError. Strict mode changes the reporting of a failed write, never whether the write succeeds.

open as a page

Why do `call`, `apply` and `bind` have no effect on an arrow function's `this`, and what happens to the optional `thisArg` argument of `Array.prototype.map` when the callback is an arrow?

level: middleimportance: should knowfreq 50%

basics

~20 s

An arrow has no this binding to overwrite — it reads this from the enclosing scope. So call, apply, bind and the thisArg of methods like map still pass a value, but the arrow never consults it. Arguments are still forwarded normally.

open as a page

Besides having no `this` of their own, what else do JavaScript arrow functions lack compared with ordinary functions, and what happens if you call `new` on one?

level: middleimportance: should knowfreq 62%

basics

~20 s

Arrow functions have no own arguments, no super, no new.target, and no prototype property, and they are not constructors: new on an arrow throws a TypeError. They also cannot be generators. All four missing bindings resolve lexically instead.

open as a page

Why does Array.prototype.slice.call(arguments) turn the arguments object into a real array, and what replaces that idiom in modern JavaScript?

level: middleimportance: should knowfreq 48%

basics

~20 s

Array methods are specified as generic: they read only a length property and integer-keyed properties from whatever this they are given. call supplies the array-like arguments object as that this. Modern code uses a rest parameter, Array.from, or spread instead.

open as a page

How does Function.prototype.bind let you preset arguments as well as this, and what is the trap in its first parameter?

level: middleimportance: should knowfreq 45%

basics

~20 s

Any arguments after bind's first are stored and prepended to every later call, giving partial application. The trap is that the first parameter is always the this value, never an argument — so presetting only arguments requires passing a placeholder receiver such as null.

open as a page

When a JavaScript method is detached and then called as a plain function, this is undefined in some code and globalThis in other code. What decides which, and why is the globalThis outcome the more dangerous bug?

level: middleimportance: should knowfreq 52%

basics

~20 s

Strict mode decides it. In strict code a plain call leaves this as undefined, so the first property access throws immediately; in sloppy code the engine substitutes globalThis, so the method silently reads and writes global properties instead of failing.

open as a page

In a JavaScript call written as `a.b.c.method()`, which object becomes `this` inside `method`, and what rule decides that?

level: middleimportance: should knowfreq 52%

basics

~20 s

Only the immediate base of the property access becomes this, so it is a.b.c — not a. Implicit binding uses the object the method was read from, and everything further left in the chain is irrelevant.

open as a page

A createLogger(config) factory captures the config object it was given. Weeks later, a caller mutates config.level after several loggers were created, and every existing logger changes behaviour. Explain why this happens and how you would design the factory so captured state cannot be changed underneath it.

level: seniorimportance: should knowfreq 40%

basics

~20 s

The closure captured the parameter binding, which holds a reference to the caller's object, so property mutations are visible immediately to every logger. To stop it, read the values you need into local bindings at creation time, or copy or freeze the object before capturing it.

open as a page

A reviewer finds `for (var i = 0; i < ids.length; i++) { load(ids[i]).then(r => { results[i] = r; }); }` and every response ends up at one index instead of spread across the array, leaving holes. Diagnose it, and say what you would change so this class of bug cannot recur.

level: seniorimportance: should knowfreq 38%

basics

~20 s

All the promise callbacks share the single var binding for i, and they run only after the loop has finished, when i equals ids.length. So every write lands on that one out-of-range index. Declare the counter with let, or build the array from returned values instead of index writes.

open as a page

Two inner functions are created in the same enclosing function: a short-lived one that reads a huge array, and a small one that is stored globally and never touches that array. Why can the huge array still be retained, and how do you fix it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Both inner functions share one environment record for the enclosing scope, and that record holds every binding any inner function captures. The stored function therefore keeps the huge array reachable even though it never reads it.

open as a page

A JavaScript module is built like this: `const mod = (function () { function a() { return 'original a'; } function b() { return a(); } return { a, b }; })();`. A caller then does `mod.a = () => 'patched a';`. Why does `mod.b()` still return 'original a', and what does that tell you about what the returned object holds?

level: seniorimportance: should knowfreq 36%

basics

~20 s

Inside the wrapper, b calls the local binding a, not the property mod.a. The returned object holds a copy of the function value at return time, so replacing the property rewires only outside callers; every internal call keeps going to the original.

open as a page

In JavaScript, what happens when you put a `function` declaration inside an `if` block, and how does the result differ between strict-mode code such as an ES module and a sloppy-mode script in a browser?

level: seniorimportance: should knowfreq 30%

basics

~20 s

In strict-mode code, including ES modules, a function declared inside a block is scoped to that block and is not visible after it. In sloppy-mode scripts, legacy web semantics also create a function-scoped variable that is only filled in when the block actually runs.

open as a page

A JavaScript app throws `ReferenceError: Cannot access 'config' before initialization` at startup. What does that specific message tell you, and how do you track down the cause?

level: seniorimportance: should knowfreq 38%

basics

~20 s

That message means the binding exists in scope but is still uninitialized: something declared with let, const, or class was accessed before its declaration ran. Find what executes during the scope's setup and reaches the name early, rather than looking for a missing declaration.

open as a page

At the top level of a classic (non-module) script, var declarations become properties of globalThis but let and const do not. What problems does that cause, and how does the picture change in an ES module?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Top-level var in a classic script creates a non-configurable property on the global object, so separate scripts can collide with each other and with built-in globals. let and const instead create bindings in a separate global scope that is not reachable as a property. In an ES module neither touches globalThis.

open as a page

You add "use strict" to the top of a large legacy browser script and parts of the page stop working. What classes of failure should you expect, and how would you roll the change out safely?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Expect two classes: parse-time syntax errors that kill the entire script before a line runs, and runtime errors on code paths that previously failed quietly. Roll out per function rather than per file, and lint for undeclared globals first.

open as a page

In a JavaScript class, what actually differs between defining `handleClick = () => { ... }` as an instance field and defining `handleClick() { ... }` as a prototype method?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The arrow field is an own, enumerable property created fresh on every instance, with this locked to that instance. The method lives once on the prototype, is non-enumerable, and takes this from the receiver at call time.

open as a page

You are writing a generic wrapper that logs every call to a method it is given. How do you make sure the wrapped method still receives the correct this, and where does that approach break down?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Return a regular function that invokes the target with fn.apply(this, args). The wrapper picks up the receiver from its own call site and forwards it. An arrow wrapper cannot do this, and calling fn(...args) drops the receiver entirely.

open as a page

A JavaScript service class passes its own tests but throws "Cannot read properties of undefined" once other modules register its methods as callbacks. How do you confirm the receiver is being lost, and what would you change so this cannot recur across the codebase?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Confirm it by checking the registration sites for a bare method reference and by reproducing the detachment directly, or by asserting the receiver inside the method. Then remove the hazard at the boundary: export closures or pre-bound handlers instead of raw methods, rather than asking every caller to remember.

open as a page

A sloppy-mode function `function User(name){ this.name = name; }` is invoked as `User('ada')` with no `new`. What is `this` inside the call, what happens to the assignment, and why is this bug hard to spot in production?

level: seniorimportance: should knowfreq 45%

basics

~20 s

With no new and no receiver, default binding applies: this is globalThis in sloppy mode, so the assignment creates a global property and the call returns undefined. Nothing throws, so the failure surfaces far from its cause.

open as a page

In a classic non-module script, what does `console.log(typeof foo); var foo = 1; function foo() {} console.log(typeof foo);` print, and why?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

It prints "function" and then "number". When the scope is entered, the function declaration wins and puts the function in the shared binding; the var declaration does not overwrite it. Later the assignment foo = 1 runs and replaces the value.

open as a page

What happens to the this value stored by Function.prototype.bind when the resulting function is invoked with the new operator?

level: middleimportance: nice to knowfreq 26%

basics

~20 s

The bound this is ignored. Construction creates a fresh object and uses that as the receiver, so new is the one call form a hard binding cannot override. Bound arguments are still prepended, and instanceof against the target function still works.

open as a page

JavaScript identifier resolution is normally static — you can tell from the source which binding a name refers to. Which two legacy constructs break that guarantee, how does each one do it, and what do engines and minifiers do in response?

level: seniorimportance: nice to knowfreq 24%

basics

~20 s

The with statement and non-strict direct eval break it. with pushes an object onto the scope chain so lookups depend on that object's properties at runtime, and direct eval can add bindings to the calling scope. Both defeat static analysis, so engines deoptimize and minifiers stop renaming.

open as a page

A memoize() helper in JavaScript keeps its cache in a closure. What memory questions would you settle before shipping it into a long-running process?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Settle four things: whether the cache is bounded in size or age, what each entry transitively retains, whether object arguments should be held weakly so callers can be collected, and how long the memoized function itself lives.

open as a page

Now that most JavaScript ships as ES modules, where does an immediately invoked function expression still earn its place in a codebase, and where has it become noise you should delete?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

Delete it wherever a module file or a block already provides the scope: wrapping a whole module body buys nothing. Keep it for code that cannot be a module — injected snippets, embeds, build-free pages — and for an async wrapper where await is otherwise illegal.

open as a page

showing 31–56 of 56