Top 20 JavaScript Interview Questions (2025 Edition)
Comprehensive JavaScript interview prep: closures, event loop, promises, prototypes, ES2024 features, and common coding challenges — with runnable examples.
javascriptinterviewweb-development
JavaScript runs in every browser and on the server via Node.js, making it one of the most in-demand skills in 2025. Whether you're interviewing at a product company or a FAANG giant, these 20 questions cover the concepts that interviewers return to most — closures, the event loop, prototypal inheritance, and modern async patterns.
Core Concepts
1. What is a closure and why is it useful?
A closure is a function that retains access to its outer scope even after the outer function has returned.
function makeCounter(start = 0) { let count = start; // this variable is "closed over" return { increment() { return ++count; }, decrement() { return --count; }, value() { return count; }, };}
Microtasks always run before the next macrotask, even if the macrotask was queued first.
3. What is the difference between var, let, and const?
| Feature | var | let | const |
|---------|-------|-------|---------|
| Scope | Function | Block | Block |
| Hoisting | Yes (undefined) | Yes (TDZ) | Yes (TDZ) |
| Re-declare | Yes | No | No |
| Re-assign | Yes | Yes | No |
TDZ = Temporal Dead Zone — the variable exists but cannot be accessed before its declaration.
console.log(x); // undefined (var hoisted)var x = 5;console.log(y); // ReferenceError: Cannot access 'y' before initializationlet y = 10;
Prefer const by default, use let when you need to reassign, and avoid var.
4. How does prototypal inheritance work?
Every JavaScript object has a hidden [[Prototype]] link to another object. When you access a property, the engine walks the prototype chain until it finds it or reaches null.
function Animal(name) { this.name = name;}Animal.prototype.speak = function () { return `${this.name} makes a sound.`;};function Dog(name) { Animal.call(this, name); // inherit own properties}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.constructor = Dog;Dog.prototype.speak = function () { return `${this.name} barks.`;};const dog = new Dog("Rex");console.log(dog.speak()); // Rex barks.console.log(dog instanceof Animal); // true
Modern code uses class syntax, which is syntactic sugar over prototype chains.
5. What is this and how is its value determined?
this depends on how a function is called, not where it is defined.
Rules in order of precedence: new binding → explicit binding (call/apply/bind) → implicit binding (method call) → default binding (global or undefined in strict mode).
Asynchronous JavaScript
6. What is the difference between a Promise and async/await?
They solve the same problem — async/await is syntactic sugar over Promises:
// Promise chainfunction fetchUser(id) { return fetch(`/api/users/${id}`) .then((res) => res.json()) .then((data) => data.name) .catch((err) => console.error(err));}// async/await — same logic, more readableasync function fetchUserAsync(id) { try { const res = await fetch(`/api/users/${id}`); const data = await res.json(); return data.name; } catch (err) { console.error(err); }}
Under the hood, async functions return Promises and await pauses execution of the async function (not the event loop).
7. How do you run multiple async operations concurrently?
Use Promise.all to run in parallel:
async function loadDashboard(userId) { // Sequential — slow (waits for each one) const user = await fetchUser(userId); const posts = await fetchPosts(userId); const friends = await fetchFriends(userId); // Parallel — fast (all start at the same time) const [user2, posts2, friends2] = await Promise.all([ fetchUser(userId), fetchPosts(userId), fetchFriends(userId), ]);}
Other combinators: Promise.allSettled (don't fail on rejection), Promise.race (first to settle wins), Promise.any (first to fulfill wins).
8. What is debouncing vs throttling?
Debounce — delay execution until the user stops triggering the event (e.g., search input).
Throttle — execute at most once per interval (e.g., scroll handler).
function myMap(arr, callback) { const result = []; for (let i = 0; i < arr.length; i++) { result.push(callback(arr[i], i, arr)); } return result;}myMap([1, 2, 3], (x) => x * 2); // [2, 4, 6]
17. What is memoization and implement it generically
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) => { console.log(`computing ${n}`); return n * n;});expensiveFn(5); // computing 5 → 25expensiveFn(5); // (cache hit) → 25
18. Implement a simple EventEmitter
class EventEmitter { #listeners = new Map(); on(event, fn) { if (!this.#listeners.has(event)) this.#listeners.set(event, []); this.#listeners.get(event).push(fn); return this; } off(event, fn) { const fns = this.#listeners.get(event) ?? []; this.#listeners.set(event, fns.filter((f) => f !== fn)); return this; } emit(event, ...args) { for (const fn of this.#listeners.get(event) ?? []) fn(...args); return this; }}const emitter = new EventEmitter();emitter.on("data", (x) => console.log("received:", x));emitter.emit("data", 42); // received: 42
19. What are WeakMap and WeakSet?
WeakMap and WeakSet hold weak references — they do not prevent garbage collection of their keys/values.
let obj = { id: 1 };const cache = new WeakMap();cache.set(obj, { computed: 42 });obj = null; // original object can now be GC'd; cache entry disappears automatically
Use cases:
Storing private data keyed to DOM elements.
Caching computed properties for objects without memory leaks.
20. Coding challenge: group anagrams together
function groupAnagrams(strs) { const map = new Map(); for (const str of strs) { const key = [...str].sort().join(""); if (!map.has(key)) map.set(key, []); map.get(key).push(str); } return [...map.values()];}groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"]);// [["eat","tea","ate"], ["tan","nat"], ["bat"]]
Time: O(n · k log k) where n = number of strings, k = max string length.
Practice Makes Perfect
Reading answers only gets you so far. You need to write JavaScript code to make it stick. On uByte you can: