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
Progress saved locally. Check off questions — saves to your browser. No account needed.
0
/ 15 Done
01What is the difference between var, let, and const?JS
varletconst
ScopeFunctionBlockBlock
HoistingHoisted, init undefinedHoisted to TDZHoisted to TDZ
ReassignYesYesNo
RedeclareYesNoNo
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.
javascript
function makeCounter() {
  let count = 0;            // private — no outside access
  return () => ++count;
}
const c = makeCounter();
c(); // 1
c(); // 2 — count persists between calls
Uses: data privacy, function factories, memoization, event handlers that remember state, and the module pattern.
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()this is obj
  • Plain call fn()undefined in strict mode (global object otherwise)
  • fn.call(x) / apply / bind → explicitly whatever you pass
  • new Fn() → the newly created object
Arrow functions have no 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.
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
To copy an object instead of sharing it: spread {...obj} (shallow) or structuredClone(obj) (deep).
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()
Promises flatten the "callback hell" of nested callbacks into a chain, with a single .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
MethodResolves whenRejects when
allEvery promise fulfillsAny single one rejects (fail-fast)
allSettledAll settleNever — always resolves
raceFirst settles (either way)If the first to settle rejects
anyFirst fulfillsAll reject (AggregateError)
Use all when you need every result, allSettled when partial failure is acceptable, race for timeouts, and any for "first success wins".
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 — returns b only when a is null or undefined (not for other falsy values like 0 or "", unlike ||).
  • obj?.a?.b — short-circuits to undefined if any link is null/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.