The short answer: JavaScript (JS) interviews come in three shapes: concept questions (types and coercion, scope and hoisting, closures, this, prototypes, the event loop, promises), “what does this print?” puzzles that test the same concepts under pressure, and coding exercises where you implement a utility such as debounce or Promise.all without libraries. Explain why the language behaves as it does, not just what it does. The answers below are checked against MDN and the TC39 proposals list, with sources at the end.
For HTML, CSS and browser questions, see our front-end developer interview questions; for React, our React interview questions; for types, our TypeScript interview questions.
Types and coercion
“What are JavaScript’s data types?”
Seven primitive types (string, number, bigint, boolean, undefined, symbol, null) and objects (which include arrays, functions, dates, maps and sets). Primitives are immutable and compared by value; objects are compared by reference. Two classic traps: typeof null is "object" (a historical quirk), and typeof NaN is "number".
“==, === and Object.is: what’s the difference?”
MDN describes three comparisons:
===(strict equality): no type conversion; “if the types differ,falseis returned.” MDN calls it “almost always the correct comparison operation to use.”==(loose equality): converts the operands to a common type first.nullandundefinedequal each other and nothing else, sonull == undefinedistruebutnull == 0isfalse. Booleans are converted to numbers, so0 == falseand'' == falseare bothtrue.Object.is(SameValue): like===, except thatObject.is(NaN, NaN)istrueandObject.is(0, -0)isfalse.
includes, Map and Set use a fourth rule, SameValueZero, which treats NaN as equal to NaN, so [NaN].includes(NaN) is true while [NaN].indexOf(NaN) is -1.
“What are truthy and falsy values?”
Falsy values: false, 0, -0, 0n, '', null, undefined and NaN (plus document.all in browsers). Everything else is truthy, including '0', [] and {}. Use ?? (nullish coalescing) instead of || when 0 or '' is a valid value: count ?? 10 keeps 0, count || 10 doesn’t.
Scope, hoisting and closures
“What is hoisting, and what is the temporal dead zone?”
Declarations are processed before code runs, but differently:
varis hoisted and initialized toundefined, so reading it before its line givesundefined.letandconstexist for the whole block but stay uninitialized until their declaration runs. MDN calls that window the temporal dead zone; reading the variable inside it “will result in aReferenceError.”- Function declarations are hoisted with their body, so you can call them before they appear; function expressions assigned to
let/constcan’t be.
console.log(a); // undefined
var a = 1;
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 2;
“Explain closures with a practical example.”
A closure is a function together with the variables of the scope it was created in. Interviewers like this use, private state:
function makeCounter() {
let count = 0;
return () => ++count;
}
const next = makeCounter();
next(); // 1
next(); // 2 (count lives on in the closure; nothing outside can change it)
Closures also explain once, memoization and the classic loop bug, which is puzzle 4 below.
this, prototypes and classes
“How is this determined?”
At call time, not where the function is written. MDN’s rules:
- Method call
obj.f():thisisobj. - Plain call
f():undefinedin strict mode (modules and classes are always strict),globalThisotherwise. call,apply,bind: setthisexplicitly;bindreturns a new function whosethis“doesn’t change,” even if you bind it again.new:thisis the new object.- Arrow functions have no
thisof their own: they “inheritthisfrom the parent scope at the time they are defined,” andcall,applyandbindcan’t change it. That’s why arrows are good for callbacks inside methods and bad as object methods.
“Explain prototypes. Are classes different?”
Every object has an internal link to a prototype; when a property isn’t found on the object, JavaScript looks up the prototype chain. class syntax is built on the same mechanism: methods go on Class.prototype, extends links the prototypes, and super calls the parent. Classes add real conveniences: they’re always in strict mode, they must be called with new, and #private fields are truly private.
“call, apply or bind?”
All three set this. call(thisArg, a, b) and apply(thisArg, [a, b]) invoke the function immediately (arguments one by one or as an array); bind(thisArg) returns a new function to call later, which is how you keep this when passing a method as a callback.
Asynchronous JavaScript
“Explain the event loop and microtasks.”
JavaScript runs one job to completion at a time. Callbacks wait in queues: tasks (timers, I/O, events) and microtasks (promise reactions and the code after await). After each task, the microtask queue is drained completely before the next task runs. That’s why a resolved promise’s callback always runs before a setTimeout(..., 0) callback; puzzle 5 below tests it with async/await.
“Compare Promise.all, allSettled, any and race.”
| Method | Fulfills when | Rejects when |
|---|---|---|
Promise.all | All inputs fulfill (array of values) | Any input rejects (first reason) |
Promise.allSettled | All inputs settle (array of { status, value / reason }) | Doesn’t reject |
Promise.any | Any input fulfills (first value) | All inputs reject (AggregateError) |
Promise.race | The first input to settle fulfills | The first input to settle rejects |
Newer helpers: Promise.withResolvers() (ES2024) returns a promise with its resolve and reject functions, and Promise.try() (ES2025) wraps a callback that may throw or return, synchronously or not, in a promise.
“How do you handle errors with async/await?”
Wrap awaited calls in try/catch (or add .catch to the returned promise). Common follow-ups: awaiting in a for loop runs requests one after another, so start them first and await Promise.all(...) to run them in parallel; and use an AbortController to cancel a fetch that’s no longer needed.
Objects, collections and copying
“Map vs plain object? What’s a WeakMap?”
A Map accepts keys of any type (including objects), keeps insertion order, has a size, and is built for frequent additions and deletions; a plain object is best for fixed, string-keyed records and JSON. A WeakMap only takes objects (or non-registered symbols) as keys and holds them weakly, so an entry doesn’t keep its key alive: useful for caching data about objects without leaking memory. Set stores unique values; ES2025 added methods such as union, intersection and difference.
“How do you deep-copy an object?”
structuredClone(value) makes a deep copy and preserves circular references; it throws a DataCloneError for values it can’t serialize, such as functions, and class instances come back as plain objects. JSON.parse(JSON.stringify(value)) is the old trick, but it drops undefined and functions and turns dates into strings. The spread operator ({ ...obj }) only copies one level.
“What’s the difference between ES modules and CommonJS?”
ES modules use import/export, are analyzed before running (which enables tree-shaking), are always strict, can use top-level await, and load asynchronously; import() loads a module on demand. CommonJS (require, module.exports) is Node.js’s original, synchronous system. Node.js supports both; new code generally uses ES modules.
What’s new in JavaScript (ES2023 to ES2026)
Interviewers rarely expect every new API, but knowing what’s current shows you keep up. From TC39’s list of finished proposals:
| Edition | Highlights |
|---|---|
| ES2023 | findLast/findLastIndex; Change Array by Copy (toSorted, toReversed, toSpliced, with); symbols as WeakMap keys |
| ES2024 | Object.groupBy and Map.groupBy; Promise.withResolvers; resizable ArrayBuffers; the RegExp v flag |
| ES2025 | Iterator helpers (map, filter, take on iterators); new Set methods; Promise.try; RegExp.escape; import attributes and JSON modules; Float16Array |
| ES2026 | Array.fromAsync; Error.isError; Math.sumPrecise; Uint8Array to and from Base64; Map “upsert”; iterator sequencing |
Temporal, the long-awaited replacement for Date, is finished and listed for ES2027.
Output puzzles (“what does this print?”)
Say your reasoning out loud; the explanation matters more than the answer.
1. Types
console.log(typeof null); // "object"
console.log(typeof NaN); // "number"
console.log([] + []); // "" (both arrays become empty strings)
console.log([] == false); // true ([] → "" → 0, false → 0)
console.log(null == 0); // false (null only loosely equals undefined)
console.log(NaN === NaN); // false
2. Hoisting
console.log(x); // undefined
var x = 1;
console.log(y); // ReferenceError: y is in the temporal dead zone
let y = 2;
3. this
'use strict';
const user = {
name: 'Ada',
greet() { return this.name; },
greetArrow: () => this?.name,
};
user.greet(); // 'Ada' (method call)
const greet = user.greet;
greet(); // TypeError: this is undefined in a plain strict-mode call
user.greetArrow(); // undefined (the arrow takes this from the outer scope)
4. Closures in loops
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i));
// 3 3 3 (one shared var i, read after the loop ends)
for (let j = 0; j < 3; j++) setTimeout(() => console.log(j));
// 0 1 2 (let creates a new binding per iteration)
5. The event loop with async/await
console.log('1');
setTimeout(() => console.log('2'), 0);
(async () => {
console.log('3');
await null;
console.log('4');
})();
Promise.resolve().then(() => console.log('5'));
console.log('6');
// 1 3 6 4 5 2
The async function runs synchronously until await, so 3 prints before 6. The code after await and the .then callback are both microtasks, queued in that order, so 4 then 5. The timer is a task, so 2 comes last.
Coding exercises with solutions
debounce: run only after the calls stop
function debounce(fn, wait) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), wait);
};
}
Typical use: a search box that fetches only after the user stops typing. Follow-ups: a leading option, and a cancel method.
throttle: run at most once per interval
function throttle(fn, wait) {
let last = 0;
return function (...args) {
const now = Date.now();
if (now - last >= wait) {
last = now;
fn.apply(this, args);
}
};
}
Typical use: scroll or resize handlers.
Promise.all from scratch
function promiseAll(items) {
return new Promise((resolve, reject) => {
const results = [];
let remaining = items.length;
if (remaining === 0) return resolve(results);
items.forEach((item, i) => {
Promise.resolve(item).then((value) => {
results[i] = value; // keep input order, not completion order
if (--remaining === 0) resolve(results);
}, reject);
});
});
}
curry
function curry(fn) {
return function curried(...args) {
return args.length >= fn.length
? fn.apply(this, args)
: (...more) => curried.apply(this, [...args, ...more]);
};
}
const add = (a, b, c) => a + b + c;
curry(add)(1)(2)(3); // 6
memoize (single argument)
function memoize(fn) {
const cache = new Map();
return (arg) => {
if (!cache.has(arg)) cache.set(arg, fn(arg));
return cache.get(arg);
};
}
Mention the trade-offs: the cache grows forever (add a size limit), and object arguments are cached by reference.
Flatten an array
arr.flat(Infinity) is the built-in answer; interviewers may ask for a recursive version:
const flatten = (arr) =>
arr.reduce((out, item) => out.concat(Array.isArray(item) ? flatten(item) : item), []);
More practice: an event emitter (on, off, emit), a concurrency-limited task runner, and deep equality. For timed coding rounds, our HackerRank interview tips cover patterns and timeboxing.
Behavioral questions for JavaScript developers
Use the STAR method with a real project:
- “Tell me about a tricky asynchronous bug you fixed.”
- “Describe a time you improved the performance of a JavaScript-heavy page.”
- “How did you introduce TypeScript, a test suite or a new build tool to a team?”
- “Tell me about code you’d write differently today.”
Frequently asked questions
What JavaScript questions are asked in interviews?
How do I prepare for a JavaScript coding interview?
What’s the difference between == and ===?
Why does a promise callback run before setTimeout(..., 0)?
Which new JavaScript features should I know in 2026?
Can Huru help me practice JavaScript interview questions?
Sources
- MDN Web Docs: Equality comparisons and sameness, let (temporal dead zone), this, Closures, JavaScript execution model, Promise and structuredClone(). Read on 28 October 2026.
- TC39, Finished Proposals (expected publication years). Read on 28 October 2026.