What is a closure, and when would you use one?
A closure is a function that retains access to its lexical scope even when invoked outside that scope. It powers data privacy, function factories, memoization, and the module pattern.
A focused list of the JavaScript interview questions that actually come up in frontend loops — from core language mechanics to the coding tasks you'll be asked to implement live. Each comes with a concise answer, and you can practice the hands-on ones in a real browser-based editor with test cases.
The fundamentals interviewers open with to gauge how well you understand the language itself.
A closure is a function that retains access to its lexical scope even when invoked outside that scope. It powers data privacy, function factories, memoization, and the module pattern.
`var` is function-scoped and hoisted with an initial value of undefined; `let` and `const` are block-scoped and live in the temporal dead zone until declared. `const` cannot be reassigned, though objects it points to remain mutable.
`this` is determined by how a function is called: the global object (or undefined in strict mode) for plain calls, the owning object for method calls, the bound value for call/apply/bind, the new instance for constructors, and the enclosing lexical `this` for arrow functions.
`==` performs type coercion before comparing, while `===` compares value and type with no coercion. Prefer `===` to avoid surprising results like 0 == "" being true.
Objects link to a prototype object via their internal [[Prototype]]. Property lookups walk this prototype chain until the property is found or the chain reaches null, which is how shared methods and inheritance work in JavaScript.
Declarations are processed before code runs: var and function declarations are hoisted (var initialized to undefined, functions fully available), while let/const are hoisted but unusable until their declaration line.
Async behavior is the single most common area where frontend candidates trip up. Expect deep follow-ups here.
The call stack runs synchronous code; async callbacks wait in queues. After the stack empties, the event loop drains all microtasks (promises, queueMicrotask) before taking the next macrotask (setTimeout, I/O, events).
Microtasks (promise callbacks) run to completion immediately after the current task and before rendering; macrotasks (timers, events) run one per loop iteration. Microtasks always have priority.
Promises give a single object representing a future value with chainable .then/.catch, flattening nested "callback hell" and centralizing error handling.
async functions return promises; await pauses execution until a promise settles, scheduling the rest of the function as a microtask. It is syntactic sugar over promise chains with synchronous-looking control flow.
Debounce waits until activity stops for N ms before firing (good for search inputs); throttle fires at most once per N ms during continuous activity (good for scroll and resize handlers).
These separate engineers who memorized syntax from those who understand mechanics.
call and apply invoke a function with an explicit this (call takes comma-separated args, apply takes an array); bind returns a new function with this permanently set, without calling it.
Transforming a function of multiple arguments into a sequence of single-argument functions, e.g. add(1)(2)(3). It enables partial application and reusable, configurable functions.
Use structuredClone(obj) in modern environments. For older targets, a recursive clone works, or JSON.parse(JSON.stringify(obj)) — though the JSON approach drops functions, undefined, Dates, and circular references.
A shallow copy (spread, Object.assign) duplicates only the top level; nested objects are still shared by reference. A deep copy recursively duplicates every level so the copy is fully independent.
Frontend loops increasingly ask you to implement these live. Practice them in a real editor rather than just reading the answer.
Tests closures, timers, and this/argument forwarding with apply.
Tests recursion versus iterative stack approaches and handling arbitrary depth.
Tests recursion, type checks, and handling arrays, objects, and circular references.
Tests promise mechanics, counting resolved values, and short-circuiting on the first rejection.
Tests closures and serializing arguments into a cache key.
Tests bubbling, event.target matching, and why delegation scales better than per-node listeners.
Closures, the event loop, the difference between var/let/const, how this works, promises and async/await, prototypal inheritance, and hands-on tasks like implementing debounce, deep clone, or Promise.all.
Master the core concepts (closures, scope, async, prototypes), then practice implementing common utilities by hand in a real editor with test cases — recall under pressure matters more than recognition.
Yes. Frontend loops emphasize the event loop, DOM and browser APIs, async UI patterns, and component-style coding far more than the abstract algorithm puzzles common in backend interviews.
Quality beats quantity. Working through 30–50 representative questions across fundamentals, async, and coding patterns — and being able to explain your reasoning — covers most frontend loops.