Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
Interview questionsJavaScriptInterview preparation

JavaScript interview questions and answers: closures to ES2025

21 JavaScript interview questions with model answers and runnable code: scope, closures, this, the event loop, promises, debounce, modules, memory leaks and recent ECMAScript features such as iterator helpers and Set methods.

Cloudsoft Zone

Updated 9 min read

Brendan Eich, creator of JavaScript, speaking on stage at Web Summit 2021 in Lisbon
Photograph: Web Summit / CC BY 2.0, via Wikimedia Commons
In this article

JavaScript interviews have a recognisable shape. Almost every one asks about scope, closures, this and the event loop, and many include a short "what does this code print?" exercise. Senior interviews add modules, memory and recent language features. The language itself moves every year through the ECMAScript standard, so answers that were complete in 2020 can now miss useful tools.

This set has 21 questions grouped by level. The model answers follow the ECMAScript specification and MDN Web Docs, and the newer features are labelled with the edition they belong to according to TC39's list of finished proposals. Code examples run in current browsers and in Node.js 24 unless a note says otherwise.

Try to answer each question aloud before you read the model answer. For the output questions, write down your prediction first.

Computer screen showing HTML and JavaScript code with highlighted functions in a developer's editor
Many JavaScript interviews include a short "what does this code print?" exercise. Photo: Martin Vorel, CC BY-SA 4.0.

Beginner questions

1. What are the differences between var, let and const?

var is function-scoped, is hoisted and initialised to undefined, and can be redeclared. let and const are block-scoped and live in the "temporal dead zone" until their declaration runs, so reading them earlier throws a ReferenceError. const prevents reassignment of the binding, not mutation: a const array can still be pushed to.

2. What are JavaScript's data types?

Seven primitive types (string, number, bigint, boolean, undefined, symbol and null) plus objects. Arrays, functions, dates and maps are all objects. Note the long-standing quirk that typeof null returns "object"; use value === null to test for it.

3. == or ===?

=== compares without type conversion; == converts types first using the abstract equality rules, which leads to results such as "" == 0 being true. Use === by default. The one common idiom with == is value == null, which matches both null and undefined.

4. What is the difference between null and undefined?

undefined means a value was never assigned: an uninitialised variable, a missing property or a function with no return. null is an intentional "no value" that a programmer assigns. The nullish operators ?? and ?. treat both the same way.

5. How do arrow functions differ from regular functions?

Arrow functions do not have their own this, arguments, super or new.target; they use the values from the surrounding scope. They cannot be used as constructors. This makes them good for callbacks inside methods, and a poor choice for object methods that need this to be the object.

6. What is the difference between ?? and ||?

a || b returns b whenever a is falsy, including 0, "" and false. a ?? b returns b only when a is null or undefined. For settings such as timeout ?? 30, where 0 is a valid value, ?? is correct.

Intermediate questions

7. What is a closure?

A closure is a function together with the variables from the scope where it was created. The inner function keeps access to those variables even after the outer function has returned. Closures are how JavaScript provides private state, and how callbacks remember their context.

JavaScript
function createCounter() {
  let count = 0;
  return {
    increment: () => ++count,
    current: () => count,
  };
}

const counter = createCounter();
counter.increment();
counter.increment();
console.log(counter.current()); // 2

8. What does this loop print, and why?

JavaScript
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// 3, 3, 3

for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}
// 0, 1, 2

With var there is one i shared by all callbacks, and by the time they run the loop has finished and i is 3. With let, each iteration gets a new binding, so each callback closes over its own value.

9. How is this determined?

By how a regular function is called: as obj.method(), this is obj; called on its own, it is undefined in strict mode (and in modules and classes, which are always strict); with new, it is the new object; with call, apply or bind, it is the value you pass. Arrow functions ignore all of these and use the enclosing this.

10. Explain the event loop, and the order of this output.

JavaScript
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
queueMicrotask(() => console.log('4'));
console.log('5');
// 1, 5, 3, 4, 2

Synchronous code runs first (1, 5). When the call stack is empty, the engine drains the whole microtask queue (promise reactions and queueMicrotask callbacks, in the order they were queued: 3, 4) before taking the next task from the task queue, such as a timer callback (2). This is why a long chain of microtasks can delay rendering and timers.

11. Compare Promise.all, allSettled, race and any.

MethodFulfils whenRejects when
Promise.allall fulfil (array of values)any one rejects (first reason)
Promise.allSettledall settle (array of status objects)never
Promise.racethe first to settle fulfilsthe first to settle rejects
Promise.anythe first to fulfilall reject (AggregateError)

12. How do you handle errors with async/await?

Wrap awaited calls in try/catch, or attach .catch() to the promise. Remember that await in a for loop runs requests one after another; to run independent requests in parallel, start them all and await Promise.all. Promise.try() (ES2025) wraps a function that may throw synchronously or return a promise, giving you one promise to handle either way.

JavaScript
async function loadDashboard(userId) {
  try {
    const [profile, orders] = await Promise.all([
      fetch(`/api/users/${userId}`).then((r) => r.json()),
      fetch(`/api/users/${userId}/orders`).then((r) => r.json()),
    ]);
    return { profile, orders };
  } catch (error) {
    console.error('Dashboard failed', error);
    throw error;
  }
}

13. How do prototypes relate to classes?

Every object has an internal link to a prototype object, and property lookups walk up this chain. class syntax is a clearer way to set up constructor functions and their prototypes; methods defined in a class body live on ClassName.prototype and are shared by all instances. Classes also add features that have no older equivalent, such as private fields (#balance) and static blocks.

14. How do you copy an object deeply?

Spread ({...obj}) and Object.assign make shallow copies: nested objects are still shared. structuredClone(obj) makes a deep copy and supports dates, maps, sets and circular references, but not functions or DOM nodes. JSON.parse(JSON.stringify(obj)) loses dates, undefined and other types, so avoid it for general cloning.

15. Write a debounce function.

Debounce delays a call until a pause of wait milliseconds, useful for search-as-you-type. Throttle, by contrast, runs at most once per interval, useful for scroll handlers.

JavaScript
function debounce(fn, wait) {
  let timerId;
  return function (...args) {
    clearTimeout(timerId);
    timerId = setTimeout(() => fn.apply(this, args), wait);
  };
}

const onSearch = debounce((term) => console.log('search', term), 300);

Advanced questions

16. ES modules or CommonJS?

ES modules (import/export) are the standard: imports are static and hoisted, bindings are live, top-level await is allowed, and code runs in strict mode. CommonJS (require/module.exports) is Node.js's original system and loads synchronously at runtime. New code should use ES modules; in Node.js, require() of a synchronous ES module is now supported as well.

17. What are WeakMap and WeakRef for?

A WeakMap holds object keys weakly: if nothing else references the key, it can be garbage-collected along with its entry. It is ideal for attaching metadata or caches to objects you do not own. WeakRef holds a weak reference to a single object; because garbage collection timing is unpredictable, use it rarely and never for program logic.

18. What are iterator helpers?

Added in ES2025, they give iterators lazy methods such as map, filter, take, drop, flatMap, reduce and toArray. Unlike array methods, they process one value at a time and do not build intermediate arrays, so they work on very large or infinite sequences.

JavaScript
function* naturals() {
  let n = 1;
  while (true) yield n++;
}

const firstEvenSquares = naturals()
  .map((n) => n * n)
  .filter((n) => n % 2 === 0)
  .take(3)
  .toArray(); // [4, 16, 36]

19. Which other recent features are worth knowing?

  • ES2025: new Set methods (union, intersection, difference, symmetricDifference, isSubsetOf, isSupersetOf, isDisjointFrom), Promise.try, RegExp.escape, import attributes and JSON modules.
  • Listed for ES2026: Array.fromAsync, Error.isError, Math.sumPrecise, Uint8Array base64 and hex conversion, iterator sequencing (Iterator.concat) and the "upsert" methods Map.prototype.getOrInsert and getOrInsertComputed.
  • Stage 4, expected in ES2027: Temporal, a modern date and time API, and explicit resource management (using declarations).

Always check browser and runtime support on MDN before using the newest features in production.

20. How do memory leaks happen in JavaScript applications?

Usually through references that are never released: event listeners that are not removed when a component unmounts, intervals that are never cleared, growing module-level caches or maps, and closures that hold large objects. Find them with the browser's memory profiler (heap snapshots), and fix them by cleaning up listeners and timers, using AbortController to remove groups of listeners, and bounding caches.

21. What is event delegation?

Instead of attaching a listener to every child element, attach one to a common parent and use event.target.closest(selector) to work out which child was clicked. It uses less memory and automatically handles elements added later. It relies on events bubbling, so it does not work for the few events that do not bubble unless you listen in the capture phase.

Brendan Eich giving a conference talk about browser competition in March 2008
Brendan Eich speaking on "browser wars: déjà vu all over again?" in 2008. Photo: Martin Kliehm, CC BY-SA 2.0.

Practice plan

  1. Write the closure, debounce and event-loop examples from memory, then run them.
  2. Change the event-loop example (add an await, nest a setTimeout) and predict the output before running it.
  3. Read the MDN pages for this, closures and the event loop; interviewers often borrow their examples.
  4. Pick one ES2025 feature and explain where you would use it in real code.

Summary

Scope, closures, this, promises and the event loop make up most JavaScript interviews. Learn those well, add a working knowledge of modules and recent standard features, and practise reading code aloud. If the role is server-side, continue with our Node.js interview questions.

Frequently asked questions

  • What JavaScript topics come up most in interviews?

    Scope and hoisting, closures, this, promises and async/await, and the event loop, often as a "what does this print?" exercise.

  • Why do promise callbacks run before setTimeout callbacks?

    After synchronous code finishes, the engine drains the entire microtask queue, which holds promise reactions, before running the next task such as a timer callback.

  • Should I mention new ECMAScript features?

    Briefly, yes. Naming ES2025 additions such as iterator helpers, Set methods and Promise.try shows you keep up, but check support on MDN before recommending them for production.

  • Is structuredClone better than JSON for deep copies?

    Usually. structuredClone() keeps dates, maps, sets and circular references; the JSON round trip loses them. Neither copies functions.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. TC39 finished proposals — TC39 / Ecma International
  2. Iterator — MDN Web Docs (Mozilla)
  3. Set.prototype.union() — MDN Web Docs (Mozilla)
  4. Map.prototype.getOrInsert() — MDN Web Docs (Mozilla)
  5. Closures — MDN Web Docs (Mozilla)
  6. ECMAScript language specification — Ecma International

Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.