Interview Prep — JavaScript Track
JavaScript Interview Questions
15 JavaScript questions — closures, the event loop, this binding, prototypal inheritance, and async/await. The concepts frontend and full-stack interviewers probe hardest. Tagged for FAANG and startups.
CoreAsyncAdvanced
15Total
6Core
4Async
5Advanced
Core JavaScript
01What is the difference between
var, let, and const?JS▾| var | let | const | |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting | Hoisted, init undefined | Hoisted to TDZ | Hoisted to TDZ |
| Reassign | Yes | Yes | No |
| Redeclare | Yes | No | No |
TDZ (Temporal Dead Zone):
let/const are hoisted but not initialised. Accessing them before their declaration throws ReferenceError rather than returning undefined. Default to const, use let when reassigning, avoid var.02What is the difference between
== and ===?JS▾== (loose) coerces both operands to the same type before comparing. === (strict) compares value and type with no coercion.
javascript
1 == "1" // true — string coerced to number 1 === "1" // false — different types 0 == false // true null == undefined // true — special-cased null === undefined // false NaN === NaN // false — NaN is never equal to anything
Rule: always use
=== unless you have a specific reason to want coercion. It avoids a whole class of subtle bugs.03What is a closure? Give a practical use.JS▾
A closure is a function that retains access to variables from the scope where it was created, even after that outer function has returned. It's how JavaScript gives you private state without classes.
Uses: data privacy, function factories, memoization, event handlers that remember state, and the module pattern.
javascript
function makeCounter() { let count = 0; // private — no outside access return () => ++count; } const c = makeCounter(); c(); // 1 c(); // 2 — count persists between calls
04How does
this work? How do arrow functions change it?JS▾this is determined by how a function is called, not where it's defined:
obj.method()→thisisobj- Plain call
fn()→undefinedin strict mode (global object otherwise) fn.call(x)/apply/bind→ explicitly whatever you passnew Fn()→ the newly created object
this of their own — they capture it lexically from the enclosing scope. That makes them ideal for callbacks but wrong for object methods that need their own this.
javascript
const obj = { name: "Ada", greet() { return `Hi, ${this.name}`; } }; const fn = obj.greet; fn(); // "Hi, undefined" — lost its receiver obj.greet.bind(obj)(); // "Hi, Ada"
05What are the falsy values in JavaScript?JS▾
There are exactly six (plus
document.all historically): false, 0, "" (empty string), null, undefined, and NaN.
Common trap:
[], {}, "0", and "false" are all truthy. An empty array in a boolean context is true. Also note 0 == false is true but [] == false is also true due to coercion.06Explain pass-by-value vs pass-by-reference in JavaScript.JS▾
JavaScript is always pass-by-value — but for objects, the "value" being passed is a reference (a pointer). So primitives are copied, while objects share the same underlying data.
To copy an object instead of sharing it: spread
javascript
function mutate(x, obj) { x = 99; // local copy — caller unaffected obj.val = 99; // same object — caller sees it obj = {}; // reassigns local ref only } let n = 1, o = { val: 1 }; mutate(n, o); n; // 1 o.val; // 99
{...obj} (shallow) or structuredClone(obj) (deep).
Async JavaScript
07Explain the event loop. What are microtasks vs macrotasks?JS▾
JavaScript is single-threaded. The event loop lets it stay non-blocking: it runs all synchronous code, then drains the entire microtask queue, then runs one macrotask, and repeats.
- Microtasks: Promise callbacks (
.then),queueMicrotask,MutationObserver - Macrotasks:
setTimeout,setInterval, I/O, UI rendering
javascript
console.log("1"); setTimeout(() => console.log("4"), 0); // macrotask Promise.resolve().then(() => console.log("3")); // microtask console.log("2"); // Output: 1, 2, 3, 4
Key insight: a microtask always runs before the next macrotask, even a
setTimeout(fn, 0) queued earlier.08What is a Promise? What are its states?JS▾
A Promise represents a value that may not be ready yet. It is always in one of three states, and once settled it never changes again:
- Pending — initial, outcome unknown
- Fulfilled — resolved with a value, triggers
.then() - Rejected — failed with a reason, triggers
.catch()
.catch() for the whole chain.
09What is
async/await and how does it relate to Promises?JS▾async/await is syntactic sugar over Promises. An async function always returns a Promise; await pauses the function until a Promise settles, without blocking the thread.
javascript
async function load(id) { try { const res = await fetch(`/api/${id}`); if (!res.ok) throw new Error(res.status); return await res.json(); } catch (err) { console.error(err); return null; } }
Trap:
await inside a loop runs sequentially. For concurrency, collect Promises and await Promise.all(...).10Compare
Promise.all, allSettled, race, and any.JS▾| Method | Resolves when | Rejects when |
|---|---|---|
all | Every promise fulfills | Any single one rejects (fail-fast) |
allSettled | All settle | Never — always resolves |
race | First settles (either way) | If the first to settle rejects |
any | First fulfills | All reject (AggregateError) |
all when you need every result, allSettled when partial failure is acceptable, race for timeouts, and any for "first success wins".
Advanced JavaScript
11Explain prototypal inheritance and the prototype chain.JS▾
Every object has an internal link (
[[Prototype]]) to another object. When you access a property JS can't find on the object, it walks up this prototype chain until it finds the property or reaches null. This is how methods are shared without copying them onto every instance.
javascript
class Animal { speak() { return "..."; } } const dog = new Animal(); dog.speak(); // found on Animal.prototype Object.getPrototypeOf(dog) === Animal.prototype; // true
class syntax is sugar over this same mechanism — JS has no separate class-based system underneath.
12Implement
debounce. How does it differ from throttle?JS▾
Debounce delays running a function until a pause in calls (e.g. search-as-you-type). Throttle runs at most once per interval regardless of call frequency (e.g. scroll handlers).
javascript
function debounce(fn, delay) { let timer; return function (...args) { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); }; } // input.addEventListener("input", debounce(search, 300));
13What is event delegation and why is it useful?JS▾
Event delegation attaches a single listener to a parent element and uses event bubbling to handle events from its children — including children added later. It's more memory-efficient than one listener per child.
javascript
list.addEventListener("click", (e) => { if (e.target.matches(".item")) { console.log("clicked", e.target.textContent); } });
14What are the nullish coalescing (
??) and optional chaining (?.) operators?JS▾a ?? b— returnsbonly whenaisnullorundefined(not for other falsy values like0or"", unlike||).obj?.a?.b— short-circuits toundefinedif any link isnull/undefined, instead of throwing.
javascript
0 || "default" // "default" — 0 is falsy 0 ?? "default" // 0 — only null/undefined trigger fallback user?.address?.city // undefined instead of TypeError
15What is currying? Show a simple implementation.JS▾
Currying transforms a function of many arguments into a chain of functions each taking one argument. It enables partial application and reusable, specialised functions.
javascript
const add = (a) => (b) => (c) => a + b + c; add(1)(2)(3); // 6 const add5 = add(5); // partial application add5(10)(20); // 35
No questions match your search.