🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

JavaScript Interview Questions 2026: Closures, Event Loop and ES2024

⏱️5 min read  ·  991 words

JavaScript interview questions in 2026 cover closures, promises, the event loop, modern ES2022+ features, TypeScript, and React patterns. This guide covers the most frequently asked questions at top tech companies with clear code examples.

Core JavaScript Questions

1. Explain closures with a practical example

// Closure = function that remembers its outer scope
function counter(start = 0) {
  let count = start; // outer variable

  return {
    increment: () => ++count,
    decrement: () => --count,
    value: () => count,
    reset: () => { count = start; }
  };
}

const c = counter(10);
console.log(c.increment()); // 11
console.log(c.increment()); // 12
console.log(c.value());     // 12
c.reset();
console.log(c.value());     // 10

// Practical use: memoization
function memoize(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}

const expensiveFn = memoize((n) => n * n);
console.log(expensiveFn(5));  // computed
console.log(expensiveFn(5));  // cached

2. What is the event loop?

// JavaScript is single-threaded, but handles async via event loop

console.log("1 - synchronous");          // runs first

setTimeout(() => console.log("2 - macro-task"), 0);  // queued in macro-task queue

Promise.resolve().then(() => console.log("3 - micro-task"));  // micro-task queue

console.log("4 - synchronous");          // runs before any async

// Output order:
// 1 - synchronous
// 4 - synchronous
// 3 - micro-task    (micro-tasks run before macro-tasks!)
// 2 - macro-task

// Event loop order:
// 1. Execute all synchronous code
// 2. Process all micro-tasks (Promises, queueMicrotask)
// 3. Pick one macro-task (setTimeout, setInterval, I/O)
// 4. Repeat

3. Explain var vs let vs const

// var — function scope, hoisted, can redeclare
function example() {
  console.log(x); // undefined (hoisted)
  var x = 1;
  if (true) {
    var x = 2; // same x (function scope)
  }
  console.log(x); // 2
}

// let — block scope, temporal dead zone, can reassign
{
  // console.log(y); // ReferenceError (TDZ)
  let y = 1;
  if (true) {
    let y = 2; // different y (block scope)
    console.log(y); // 2
  }
  console.log(y); // 1
}

// const — block scope, cannot reassign (but objects are mutable)
const arr = [1, 2, 3];
arr.push(4); // OK — mutating object, not reassigning reference
// arr = []; // TypeError — cannot reassign

4. What is prototypal inheritance?

// Every object has a [[Prototype]] (accessed via __proto__ or Object.getPrototypeOf)
const animal = {
  speak() { return `${this.name} makes a sound`; }
};

const dog = Object.create(animal);
dog.name = 'Rex';
console.log(dog.speak()); // "Rex makes a sound" — inherited!

// Class syntax (syntactic sugar over prototypes)
class Animal {
  constructor(name) { this.name = name; }
  speak() { return `${this.name} makes a sound`; }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
  speak() { return `${this.name} barks`; }
}

const d = new Dog('Rex', 'Labrador');
console.log(d.speak());            // "Rex barks"
console.log(d instanceof Dog);     // true
console.log(d instanceof Animal);  // true

Async JavaScript

5. Promise.all vs Promise.allSettled vs Promise.race vs Promise.any

const p1 = fetch('/api/users');
const p2 = fetch('/api/posts');
const p3 = fetch('/api/missing');  // will fail

// Promise.all — fails if ANY fails
const [users, posts] = await Promise.all([p1, p2]);  // throws if p3 fails

// Promise.allSettled — waits for all, doesn't fail
const results = await Promise.allSettled([p1, p2, p3]);
results.forEach(r => {
  if (r.status === 'fulfilled') processData(r.value);
  else console.error(r.reason);
});

// Promise.race — resolves/rejects with FIRST settled
const fastest = await Promise.race([p1, p2]);  // whichever resolves first

// Promise.any — resolves with FIRST fulfilled (ignores rejections)
const first = await Promise.any([p1, p2, p3]);  // first success

6. What are WeakMap and WeakSet used for?

// WeakMap — keys are objects, garbage collected when object is unreachable
const cache = new WeakMap();

function processData(obj) {
  if (cache.has(obj)) return cache.get(obj);
  const result = heavyComputation(obj);
  cache.set(obj, result);  // obj can still be GC'd
  return result;
}

// WeakSet — stores object references, auto-cleaned
const seen = new WeakSet();

function markAsSeen(obj) {
  seen.add(obj);
}

// Key: WeakMap/WeakSet don't prevent garbage collection
// Perfect for: private data, caching with automatic cleanup, tracking objects

Modern JavaScript (ES2020-2024)

7. Optional chaining (?.) and nullish coalescing (??)

const user = { profile: { name: "Alice" } };

// Optional chaining — stop chain on null/undefined
const name = user?.profile?.name;       // "Alice"
const city = user?.address?.city;       // undefined (no error!)
const len = user?.profile?.name?.length; // 5

// Method calls
const result = user?.getProfile?.();    // undefined if method doesn't exist

// Array access
const first = arr?.[0];                 // undefined if arr is null

// Nullish coalescing — default for null/undefined only
const displayName = user?.name ?? "Anonymous";  // "Anonymous"
const count = 0 ?? 10;   // 0 (0 is not null/undefined)
const count2 = 0 || 10;  // 10 (0 is falsy — different behavior!)

// Nullish assignment
user.name ??= "Default";  // assign only if null/undefined

8. What are WeakRef and FinalizationRegistry?

// WeakRef — hold weak reference to object (allows GC)
let obj = { data: "important" };
const ref = new WeakRef(obj);

// Later...
const deref = ref.deref();
if (deref) {
  console.log(deref.data); // might be undefined if GC'd
}

// FinalizationRegistry — callback when object is GC'd
const registry = new FinalizationRegistry((heldValue) => {
  console.log(`Cleaned up: ${heldValue}`);
  // Clean up external resources associated with obj
});

let obj2 = { data: "value" };
registry.register(obj2, "obj2-key");
obj2 = null; // obj2 eligible for GC — callback will fire eventually

Common Tricky Questions

9. What is the output? (Explain why)

// Question 1
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// Output: 3, 3, 3
// Why: var is function-scoped, all closures share same i

// Fix with let
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// Output: 0, 1, 2 (let creates new binding each iteration)

// Question 2
console.log(typeof null); // "object" (historical bug!)
console.log(typeof undefined); // "undefined"
console.log(null == undefined); // true (loose equality)
console.log(null === undefined); // false (strict equality)

// Question 3
console.log(0.1 + 0.2 === 0.3); // false (floating point!)
console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON); // true

JavaScript interview success: understand the event loop deeply, know closure and scope, explain prototypal inheritance clearly, and demonstrate async mastery with promises and async/await. Modern ES features (optional chaining, nullish coalescing, WeakMap) show you keep up with the language. Always explain the “why” not just the syntax.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা