Advanced JavaScript Interview Questions
12 JavaScript interview questions for senior and lead developers (5+ years): the event loop in depth, WeakMap, structuredClone, private fields, prototypes, Symbols, Proxy and memory leaks. Each answer explains why the other options are wrong.
CoversArraysClassesCollectionsCopyingCore JavaScriptEvent loopLoopsMemoryMetaprogrammingObjectsPerformancePrototypesProxySymbols
By Deepak KumarPublished Answers checked by Deepak
Take it as a timed test
- 12 questions
- 18 min
- Pass mark 60%
- Answers shown at the end
No sign-up. Your answers and scores stay in this browser.
This pro paper has 12 JavaScript questions for senior and lead developers with roughly 5+ years of experience. It covers the event loop in depth, WeakMap, structuredClone, private fields, prototypes, Symbols, Proxy and memory leaks. Ten questions are on this page with full explanations; the timed test also includes two questions that aren't shown here, so practise for understanding rather than memory.
How should you use this paper?
Answer each practice question before opening its explanation, and say your reasoning out loud the way you would in an interview. Then take the timed test: 18 minutes, a pass mark of 60%, with answers shown at the end. Your report breaks your score down by topic and difficulty, and you can retry only the questions you missed.
Practice questions with answers
Try each one before opening the answer. The explanation covers why the other options are wrong. The timed test also uses 2 questions that aren't shown here, so practise for understanding, not memory.
In what order do the logs run when a promise callback schedules another timer?
setTimeout(() => console.log("t1"), 0);
Promise.resolve()
.then(() => {
console.log("p1");
setTimeout(() => console.log("t2"), 0);
})
.then(() => console.log("p2"));
console.log("s");Show answer and explanation
Answer: A. s p1 p2 t1 t2
The script runs first and logs s. Then the engine drains the microtask queue completely: p1 runs (and queues timer t2 behind t1), and its then queues another microtask that logs p2, which still runs before any timer. Only then do tasks run in the order they were queued: t1, then t2. Microtasks always finish before the next task.
Why would you store per-object metadata in a WeakMap instead of a Map?
Show answer and explanation
Answer: A. A WeakMap doesn't keep its keys alive, so entries disappear when the object is garbage-collected
A Map holds strong references, so an object used as a key can never be garbage-collected while the map exists, which leaks memory in long-lived caches. WeakMap keys are held weakly: when nothing else references the object, the entry goes away too. The trade-off is that keys must be objects and a WeakMap can't be iterated or sized.
What happens when you run structuredClone({ run() {} })?
Show answer and explanation
Answer: A. It throws a DataCloneError because functions can't be cloned
structuredClone deep-copies data that can be serialised: objects, arrays, dates, maps, sets, typed arrays and circular references. Functions, DOM nodes and class prototypes can't be cloned, so it throws a DataCloneError rather than dropping them quietly. That's a key difference from JSON.parse(JSON.stringify(x)), which silently omits functions and turns dates into strings.
What does the #x in obj brand check print here?
class A {
#x = 1;
static has(obj) {
return #x in obj;
}
}
console.log(A.has(new A()), A.has({}));Show answer and explanation
Answer: A. true false
Private fields are only installed on objects created by the class. #x in obj is an ergonomic brand check: it returns true when the object has that private field and false otherwise, without throwing. An instance of A has #x, a plain object doesn't. It's the safe way to check whether an object really is your class's instance.
A single-page app slowly uses more memory on each route change. What is the most likely cause?
Show answer and explanation
Answer: A. Listeners or intervals that are never removed and keep references to old components
Each route change mounts new components. If the old ones added window listeners, intervals or subscriptions and never cleaned them up, those callbacks keep their closures, and the DOM and data they reference, alive. Memory grows on every navigation. Remove listeners on unmount and clear timers. Heap snapshots in DevTools, compared before and after navigating, show what is being retained.
What does calling an inherited method and listing own keys print?
const proto = { greet() { return "hi " + this.name; } };
const o = Object.create(proto);
o.name = "Ana";
console.log(o.greet(), Object.keys(o));Show answer and explanation
Answer: A. hi Ana ["name"]
greet lives on the prototype, so o.greet is found by walking up the prototype chain, and this is o because of how it is called. Object.keys lists only the object's own enumerable properties, so it returns just ["name"]. Methods shared through the prototype don't appear, which is why a for...in loop needs care.
Which statements about Symbol are true? Select all that apply.
Show answer and explanation
Answer: A. Every Symbol() call returns a new, unique value + B. Symbols can be used as object property keys + C. Symbol.for('id') returns the same symbol for the same key across the app
Each Symbol() is unique, even with the same description, which makes symbols safe keys that can't clash with string keys. Symbol.for uses a global registry, so the same key returns the same symbol. Symbol-keyed properties are skipped by JSON.stringify, Object.keys and for...in; use Object.getOwnPropertySymbols to see them.
for...in over an array gives you its values.
TrueFalse
Show answer and explanation
Answer: False
False. for...in loops over enumerable property keys, so for an array you get the indexes as strings ("0", "1"...) and also any enumerable properties added to the array or its prototype. Use for...of for values, or entries() when you need both index and value. for...in is meant for plain objects.
How does queueMicrotask(fn) differ from setTimeout(fn, 0)?
Show answer and explanation
Answer: A. The microtask runs as soon as the current script finishes, before timers and before the browser renders
Microtasks run right after the current call stack empties, before the event loop picks up the next task, so they also run before rendering. setTimeout(fn, 0) queues a task, which runs later, and browsers clamp nested timers to at least 4 ms. A long chain of microtasks can block rendering, so use them for small follow-up work only.
Match each Proxy trap to the operation it intercepts.
- get
- set
- has
- deleteProperty
- The
deleteoperator - Reading a property
- The
inoperator - Assigning a property
Show answer and explanation
Answer: get → Reading a propertyset → Assigning a propertyhas → The in operatordeleteProperty → The delete operator
A Proxy wraps an object and lets traps intercept operations on it. get and set handle reads and writes, which is how many reactive state libraries track changes. has intercepts key in obj, and deleteProperty intercepts delete obj.key. Inside traps, Reflect methods perform the default behaviour.
Answered them all? Take the same questions against the clock and get a topic-by-topic report.
Start the timed test