JavaScript Interview Questions & Answers (2026)
JavaScript interviews test the language's three notorious footguns: this-binding, the prototype chain, and the event loop. This guide covers 16 questions we've seen candidates hit at Meta, Google, Stripe, Vercel, Shopify, and startup frontend interviews in the last 12 months. Grouped by difficulty. Frameworks (React, Vue) live in the React-specific guide.
Beginner questions
1. Explain closures. Beginner
A closure is a function bundled with the lexical environment it was declared in. When the inner function survives past the outer function's return, it keeps a reference to the outer function's variables — those variables are not garbage collected. Practical use: private state, memoization, callbacks that need setup context. Common bug: closing over a loop variable declared with var — every closure sees the final value. let fixes it by creating a new binding per iteration.
function counter() {
let n = 0;
return () => ++n;
}
const tick = counter();
tick(); tick(); tick(); // 3
2. What's the difference between var, let, and const? Beginner
var is function-scoped and hoisted — you can reference it before its declaration and get undefined. let and const are block-scoped and hoisted to the temporal dead zone — referencing them before declaration throws ReferenceError. const binds the identifier immutably but not the value — const arr = []; arr.push(1) is legal.
3. Explain event delegation. Beginner
Attach one event listener to a parent element instead of many on individual children. Rely on event bubbling — clicks originate at the target, bubble up through ancestors. Use event.target to know which child was clicked. Wins: fewer listeners (memory), works for dynamically-added children (no re-binding), simpler cleanup.
list.addEventListener('click', (e) => {
const item = e.target.closest('li');
if (!item) return;
handle(item.dataset.id);
});
4. What's the difference between == and ===? Beginner
=== is strict equality — same type AND same value. == is loose equality — coerces types before comparing, which produces surprises: 0 == '' is true, [] == false is true, null == undefined is true. Always use === unless you specifically want the null-undefined-both-true shortcut, in which case comment it.
5. Explain hoisting. Beginner
The JavaScript engine moves declarations to the top of their scope before executing code. var declarations are hoisted with an initial value of undefined. Function declarations are fully hoisted — you can call them before the source line. let and const are hoisted but not initialized — accessing them before the declaration throws. class declarations behave like let.
Practice these live, in your voice
MiPrep's practice mode turns your resume into a rehearsed answer set. Talk through the idioms the way top-tier interviewers score.
Download MiPrep 🔒 Interview audio is never stored on our serversIntermediate questions
6. How does the event loop work? Intermediate
Single-threaded event loop. Sync code runs to completion, then the loop drains the microtask queue (Promise callbacks, queueMicrotask), then processes one macrotask (setTimeout, setInterval, I/O), then drains microtasks again. Rendering happens between macrotasks in the browser. Practical implication: a Promise chain can starve rendering — awaited work stays in microtasks. setTimeout(fn, 0) yields to rendering; Promise.resolve().then(fn) does not.
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
// Output: 1, 4, 3, 2
7. What is 'this' bound to? Intermediate
Four rules, in priority order. (1) new binding — new fn() binds this to the new object. (2) explicit binding — fn.call(x) or fn.bind(x) sets this to x. (3) implicit binding — obj.fn() sets this to obj. (4) default binding — plain fn() gives undefined in strict mode, global in sloppy. Arrow functions have no this of their own — they close over the enclosing this lexically. That's why event handlers written as arrows fail to reference the instance.
8. Explain async/await. Intermediate
async marks a function as returning a Promise. await suspends the function until the awaited Promise settles, then resumes with the resolved value (or throws the rejection). Under the hood, it's syntactic sugar over Promise.then chains. Common mistake: await in a for-loop when the iterations are independent — use Promise.all for parallel. Correct: await Promise.all(items.map(process)).
// Serial (slow)
for (const url of urls) await fetch(url);
// Parallel (fast)
await Promise.all(urls.map(u => fetch(u)));
9. What is the prototype chain? Intermediate
Every object has an internal [[Prototype]] link to another object. Property lookup walks the chain until it finds the property or hits null. class syntax is sugar over prototype-based inheritance. class Dog extends Animal sets Dog.prototype.[[Prototype]] = Animal.prototype. Object.create(proto) creates an object with the given prototype directly.
10. Explain Promise.all vs Promise.allSettled vs Promise.race. Intermediate
Promise.all — resolves when all resolve, rejects fast on first rejection (short-circuit). Promise.allSettled — waits for every promise regardless of outcome, returns [{status, value|reason}]. Promise.race — resolves/rejects with the first settled promise (fastest, or first failure). Promise.any — resolves with first fulfillment, rejects only if all reject. Choose based on failure semantics: use allSettled for parallel work where partial failure is acceptable.
11. What is a debounce vs a throttle? Intermediate
Debounce — wait until N ms after the last call, then fire once. Good for search-as-you-type. Throttle — allow at most one call per N ms. Good for scroll handlers. Both use setTimeout / Date.now, but produce different UX. Implement debounce as: return a wrapper that clears the pending timer and sets a new one; throttle as: check timestamp of last call.
12. What is currying? Intermediate
Transforming a function of N args into a chain of N unary functions. curry(add)(2)(3) === add(2, 3). Useful for partial application and building point-free pipelines. Trivial for fixed arity: const curry = fn => a => b => fn(a, b). Variadic curry is a puzzle interviewer favorite — accumulate args until called with the expected count.
13. How would you deep-clone an object? Intermediate
Modern answer: structuredClone(obj) — built into browsers and Node 17+, handles Dates, Maps, Sets, circular refs. JSON.parse(JSON.stringify(obj)) is the interview cliche but silently drops functions, undefined values, Dates (becomes string), Maps, Sets, and throws on cycles. Lodash cloneDeep is fine but ships a dependency. For your interviewer: mention all three, name the tradeoffs.
Advanced questions
14. How does JavaScript handle memory management? Advanced
Garbage collection via mark-and-sweep — the GC starts at roots (global object, current stack) and walks references. Unreachable objects are freed. Common leaks: closures holding large objects, forgotten event listeners on removed DOM nodes, growing arrays used as caches without eviction. Use WeakMap / WeakSet for reference-holding without preventing GC. In Node, use --inspect and Chrome DevTools heap snapshots to find retainers.
15. What are ES modules and how do they differ from CommonJS? Advanced
ESM (import/export) — static, top-level, live bindings. Statically analyzable so bundlers can tree-shake. CommonJS (require/module.exports) — dynamic, runtime evaluation, snapshot semantics (require caches the exported object at the time of first require). Bug pattern: circular imports work in CommonJS (you get a partial object) but break in ESM (you get undefined until initialization completes).
16. What is a Web Worker? Advanced
A JavaScript execution context on a separate OS thread. Communicates with the main thread via postMessage — arguments are structured-cloned across the boundary (or transferred if using Transferable objects like ArrayBuffer). Use for CPU-bound work (image processing, WASM computation) that would otherwise block rendering. Cannot access DOM. Modern alternative: OffscreenCanvas for graphics workers.
Common mistakes candidates make
- Forgetting to await a Promise — silently returns the Promise object, later code sees pending state.
- Mutating props or state directly in React — bypass reactivity.
- Using == with the intent of ===.
- Closure over loop variable declared with var — every closure sees the final value.
- Attaching event listeners without removing them — memory leak in single-page apps.
Study strategy
Two-week plan. Week 1: rebuild Promise, debounce, throttle, curry, and deep-clone from scratch — one per day. Read the ECMA-262 event-loop section (dense but load-bearing). Week 2: 30 medium LeetCode problems tagged JavaScript. In your real interview, when asked 'implement X', write the signature first and check the signature with the interviewer before writing the body.
Do timed mocks with MiPrep before the real thing
Upload your resume and target job description. MiPrep generates a rehearsed answer set in your voice from your own projects — so mock interviews sound like real ones.
Get MiPrep — free 🔒 Interview audio is never stored on our servers