Intermediate JavaScript Interview Questions
12 JavaScript interview questions for developers with some experience (2 to 5 years): closures, this and bind, async/await order, event delegation, Promise combinators and copying arrays. Each answer explains why the other options are wrong.
CoversArraysArrow functionsAsync codeClosuresCopyingCore JavaScriptDOMEvent loopEventsFunctionsObjectsPromisesSyntaxthis
By Deepak KumarPublished Answers checked by Deepak
Take it as a timed test
- 12 questions
- 15 min
- Pass mark 60%
- Answers shown at the end
No sign-up. Your answers and scores stay in this browser.
This intermediate paper has 12 JavaScript questions for developers with some experience with roughly 2 to 5 years of experience. It covers closures, this and bind, async/await order, event delegation, Promise combinators and copying arrays. 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: 15 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.
What does calling two counters made by the same factory print?
function makeCounter() {
let count = 0;
return () => ++count;
}
const a = makeCounter();
a();
a();
const b = makeCounter();
console.log(a(), b());Show answer and explanation
Answer: A. 3 1
Each call to makeCounter creates a new count variable, and the returned function closes over its own copy. Counter a has been called twice, so its third call returns 3. Counter b is new and starts from 0, so it returns 1. This is how closures give each instance private state without classes.
What does fn.bind(obj) return?
Show answer and explanation
Answer: A. A new function whose this is always obj
bind doesn't call the function. It returns a new function with this fixed to the given object, plus any arguments you pre-fill. The original function is unchanged. call and apply are the ones that invoke straight away. bind is useful for callbacks, such as passing a method to setTimeout without losing its object.
What does an arrow function returned from a method print for this.name?
const obj = {
name: "Ana",
greet() {
return () => this.name;
},
};
console.log(obj.greet()());Show answer and explanation
Answer: A. Ana
The arrow function has no this of its own, so it uses the this of greet at the moment it was created. greet was called as obj.greet(), so this was obj, and the arrow function returns "Ana" even though it is called on its own later. A regular function in its place would lose this.
What is event delegation?
Show answer and explanation
Answer: A. Attaching one listener to a parent and using event.target to handle clicks on its children
Because events bubble, a single listener on a parent can handle clicks on all of its children, including ones added later. You check event.target (often with closest()) to see which child was clicked. It uses less memory than one listener per item and keeps working for dynamic lists, which is why it comes up in performance questions.
In what order do the numbers print around an await?
async function f() {
console.log(1);
await null;
console.log(2);
}
f();
console.log(3);Show answer and explanation
Answer: A. 1 3 2
An async function runs synchronously until its first await, so 1 is logged first. The await pauses f and schedules the rest as a microtask, and control returns to the caller, which logs 3. When the synchronous code finishes, the microtask resumes f and logs 2. Even awaiting a non-promise like null pauses the function.
Which of these create a shallow copy of an array a? Select all that apply.
Show answer and explanation
Answer: A. [...a] + B. a.slice() + C. Array.from(a)
Spread, slice() and Array.from all create a new array with the same items, so pushing to the copy doesn't affect the original. They are shallow: objects inside are shared, not cloned. const b = a copies only the reference, so both names point to the same array. For a deep copy, use structuredClone(a).
Promise.allSettled rejects if any of its promises rejects.
TrueFalse
Show answer and explanation
Answer: False
False. Promise.allSettled always waits for every promise and then resolves with an array of results, each { status: 'fulfilled', value } or { status: 'rejected', reason }. It never rejects because of an input. That makes it the right choice when you want every outcome, such as sending several emails and reporting which ones failed.
If user.address is undefined, what does user.address?.city evaluate to?
Show answer and explanation
Answer: A. undefined, with no error
Optional chaining stops and returns undefined as soon as the value before ?. is null or undefined, instead of throwing "Cannot read properties of undefined". It's often combined with nullish coalescing for a default: user.address?.city ?? 'Unknown'. Don't overuse it: if a value should always exist, a loud error is more useful than a silent undefined.
What does ["1", "2", "3"].map(parseInt) print?
console.log(["1", "2", "3"].map(parseInt));Show answer and explanation
Answer: A. [1, NaN, NaN]
map calls its callback with three arguments: the item, its index and the array. parseInt takes a second argument, the radix, so the calls become parseInt('1', 0), parseInt('2', 1) and parseInt('3', 2). Radix 0 means default base 10 (1), base 1 is invalid (NaN), and '3' isn't a binary digit (NaN). Use map(Number) or an arrow function.
Match each Promise combinator to how it settles.
- Promise.all
- Promise.allSettled
- Promise.race
- Promise.any
- Settles like whichever promise settles first
- Fulfils with the first success; rejects only if all fail
- Fulfils when all fulfil; rejects on the first rejection
- Waits for every promise and reports each outcome
Show answer and explanation
Answer: Promise.all → Fulfils when all fulfil; rejects on the first rejectionPromise.allSettled → Waits for every promise and reports each outcomePromise.race → Settles like whichever promise settles firstPromise.any → Fulfils with the first success; rejects only if all fail
all fails fast, allSettled never rejects, race follows whichever settles first (success or failure), and any waits for the first success, rejecting with an AggregateError only when every promise fails. A common real use of race is a timeout: race the request against a promise that rejects after a few seconds.
Answered them all? Take the same questions against the clock and get a topic-by-topic report.
Start the timed test