learn deep. ship safe.Practice testsFollow ByteMunSubscribe
JavaScript · All levels

Top 20 JavaScript interview questions with answers: core JS, Node.js and React

20 JavaScript interview questions with answers: 10 on core JavaScript, 5 on Node.js and 5 on React, covering closures, hoisting, the event loop, promises, Express errors and React state.

CoversArraysAsync codeClosuresCore JavaScriptEqualityError handlingEvent loopExpressFunctionsHoistingHooksImmutabilityListsNode.jsPerformancePromisesReactRenderingScopeStatethisTypesWorker threads

By Published Answers checked by Deepak

Take it as a timed test

  • 20 questions
  • 20 min
  • Pass mark 60%
  • Answers shown at the end
Start the timed test

No sign-up. Your answers and scores stay in this browser.

This quiz follows the shape of a typical JavaScript developer interview: 10 questions on the core language, then 5 on Node.js and 5 on React. It mixes single-answer, select-all, true-or-false, matching and predict-the-output questions, at fresher and mid level.

What does it cover?

  • Core JavaScript (10): typeof, equality and falsy values, hoisting, closures, this in arrow functions, the event loop, Promise.all and array methods.
  • Node.js (5): the single thread and libuv, nextTick versus promises versus setImmediate, blocking calls, async errors in Express and CPU-heavy work.
  • React (5): keys, batched state updates, hooks, stale closures in effects and immutable state updates.

How should you use it?

Go through the practice questions first and commit to an answer before you open each explanation. Then take the timed test: 20 questions in 20 minutes, with a pass mark of 60%. Your report shows which area you lost marks in, so you know whether to revise the core language, Node.js or React first, 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.

Q1What does this code output?easy

What does typeof null return?

javascript
console.log(typeof null);
  1. "object"
  2. "null"
  3. "undefined"
  4. It throws a TypeError
Show answer and explanation

Answer: A. "object"

typeof null returns "object". This is a bug from the first version of JavaScript that was never fixed, because changing it would break existing websites. null is still a primitive value, not an object. To check for it, compare directly with value === null. "undefined" is what typeof returns for an undeclared or unassigned variable, and typeof never throws, even for names that were never declared.

Q2What does this code output?medium

What does a var loop with setTimeout log?

javascript
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
  1. 3 3 3
  2. 0 1 2
  3. undefined undefined undefined
  4. 0 0 0
Show answer and explanation

Answer: A. 3 3 3

var is function-scoped, so all three callbacks close over the same i. The timers run only after the loop has finished, when i is already 3, so it logs 3 3 3. Changing var to let prints 0 1 2, because let creates a new binding for each loop iteration. Interviewers use this to test both closures and how the event loop delays timer callbacks.

Q3What does this code output?easy

What do 0 == '' and 0 === '' return?

javascript
console.log(0 == '', 0 === '');
  1. true false
  2. false false
  3. true true
  4. false true
Show answer and explanation

Answer: A. true false

== converts both sides to a common type before comparing: the empty string becomes the number 0, so 0 == '' is true. === compares without any conversion, and a number is never strictly equal to a string, so it is false. This hidden conversion is why most style guides ask for === everywhere. The common exception is value == null, which matches both null and undefined.

Q4True or falsemedium

Variables declared with let are not hoisted at all.

TrueFalse

Show answer and explanation

Answer: False

False. let and const declarations are hoisted to the top of their block, but they are not initialised. From the start of the block until the declaration line runs, the variable is in the temporal dead zone, and reading it throws a ReferenceError. var is also hoisted, but it is initialised to undefined, which is why reading a var early gives undefined instead of an error.

Q5Choose one answereasy

What is a closure in JavaScript?

  1. A function together with the variables from the scope it was created in, which it can still use after that scope has finished
  2. A function that has been closed with Object.freeze so it cannot be changed
  3. A block of code wrapped in curly braces
  4. A function that runs once and then deletes itself
Show answer and explanation

Answer: A. A function together with the variables from the scope it was created in, which it can still use after that scope has finished

Every function keeps a reference to the variables of the scope where it was defined. When an outer function returns an inner function, the inner one can still read and update those variables. That combination is a closure. Closures power private state (a counter only its own functions can change), event handlers, memoisation and many module patterns. Freezing, curly-brace blocks and run-once functions are unrelated ideas that only sound similar.

Q6What does this code output?medium

In what order do a timer, a promise and two logs run?

javascript
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');
  1. A D C B
  2. A B C D
  3. A D B C
  4. A C D B
Show answer and explanation

Answer: A. A D C B

Synchronous code runs first, so A and D are logged straight away. Promise callbacks go into the microtask queue, and timer callbacks go into the task (macrotask) queue. After the current script finishes, the engine empties the whole microtask queue before it takes the next task, so C comes before B, even though the timer was set to 0 ms. A delay of 0 means "as soon as possible after this", not "immediately".

Q7Select all that applymedium

Which of these values are falsy? Select all that apply.

  1. 0
  2. "0"
  3. []
  4. null
  5. NaN
  6. "" (an empty string)
Show answer and explanation

Answer: A. 0 + D. null + E. NaN + F. "" (an empty string)

JavaScript has a short list of falsy values: false, 0, -0, 0n, "", null, undefined and NaN. Everything else is truthy. That includes the string "0", because it is a non-empty string, and an empty array [], because every object is truthy. This is why if (list) does not detect an empty array. Check list.length instead.

Q8Choose one answermedium

What value does this have inside an arrow function?

  1. The this of the scope where the arrow function was defined
  2. The object the arrow function was called on
  3. Always the global object
  4. A new empty object created for each call
Show answer and explanation

Answer: A. The this of the scope where the arrow function was defined

Arrow functions do not have their own this. They take it from the surrounding scope at the point where they are defined, and call, apply or bind cannot change it. That makes them a good fit for callbacks inside methods. It also makes them a poor choice for object methods that need the object as this, and they cannot be used as constructors. A regular function, by contrast, gets this from how it is called.

Q9Choose one answermedium

How does Promise.all behave if one of the promises rejects?

  1. It rejects straight away with that error, without waiting for the others
  2. It waits for every promise and returns an array of results and errors
  3. It ignores the rejected promise and resolves with the rest
  4. It retries the rejected promise once
Show answer and explanation

Answer: A. It rejects straight away with that error, without waiting for the others

Promise.all fails fast: as soon as any promise rejects, the combined promise rejects with that error. The other operations keep running, but their results are discarded. When you need every outcome, use Promise.allSettled, which waits for all of them and returns { status, value } or { status, reason } for each. Promise.any resolves with the first success, and Promise.race settles with whichever promise settles first.

Q10Match each item on the lefteasy

Match each array method to what it returns.

  1. map
  2. filter
  3. reduce
  4. forEach
  • A new array with only the items that pass a test
  • A new array with every item transformed
  • undefined: it only runs a function for each item
  • A single value built up from all the items
Show answer and explanation

Answer: map → A new array with every item transformedfilter → A new array with only the items that pass a testreduce → A single value built up from all the itemsforEach → undefined: it only runs a function for each item

map returns a new array of the same length with each item transformed. filter returns a new array containing only the items whose callback returned a truthy value. reduce folds the array into one value, such as a sum or an object built from the items. forEach returns undefined, so it is only for side effects. A common mistake is chaining .forEach().map() and expecting an array back.

Q11True or falsemedium

Node.js cannot do any work in parallel because JavaScript runs on a single thread.

TrueFalse

Show answer and explanation

Answer: False

False. Your JavaScript runs on one main thread, but Node.js hands slow I/O to the operating system and to libuv's thread pool, which handles file system work, DNS lookups, compression and some crypto. The event loop picks up the results when they are ready. That is how one Node.js process serves thousands of connections at once. For CPU-heavy JavaScript you can add real parallelism with worker_threads or by running several processes.

Q12What does this code output?medium

Run as a CommonJS script (node app.js), in what order does this log sync, nextTick, promise and immediate?

javascript
setImmediate(() => console.log('immediate'));
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('promise'));
console.log('sync');
  1. sync, nextTick, promise, immediate
  2. sync, promise, nextTick, immediate
  3. nextTick, sync, promise, immediate
  4. sync, immediate, nextTick, promise
Show answer and explanation

Answer: A. sync, nextTick, promise, immediate

Synchronous code always runs first, so sync comes first. When it finishes, Node.js empties the process.nextTick queue before the promise microtask queue, so nextTick comes before promise. setImmediate callbacks run in the check phase of the next turn of the event loop, after all microtasks, so immediate is last. Overusing nextTick can starve I/O, which is why queueMicrotask is usually the better choice. Watch out: in an ES module (.mjs or "type": "module"), top-level code already runs as a microtask, so there promise logs before nextTick.

Q13Choose one answereasy

Why should you avoid fs.readFileSync inside an HTTP request handler?

  1. It blocks the event loop, so every other request waits until the file has been read
  2. It cannot read files larger than 1 MB
  3. It only works with JSON files
  4. It is deprecated and will be removed from Node.js
Show answer and explanation

Answer: A. It blocks the event loop, so every other request waits until the file has been read

A synchronous call stops the single JavaScript thread until the disk read finishes. In a server, that means every other user's request waits too, and under load latency climbs for everyone. Use fs.promises.readFile with await, or a stream for large files. Sync APIs are not deprecated, have no 1 MB limit and read any file type: they are fine in startup code and CLI scripts, where nothing else is waiting.

Q14Select all that applyhard

In Express 4, which of these correctly send an error from an async route handler to the error-handling middleware? Select all that apply.

  1. Wrap the handler body in try/catch and call next(err) in the catch
  2. Attach .catch(next) to the promise the handler returns
  3. Do nothing: Express 4 forwards rejected promises automatically
  4. Call process.exit(1) so the error is logged
Show answer and explanation

Answer: A. Wrap the handler body in try/catch and call next(err) in the catch + B. Attach .catch(next) to the promise the handler returns

Express 4 does not look at the promise an async handler returns, so a rejection never reaches your error middleware: the request hangs, and Node.js logs an unhandled rejection. Catch it yourself and call next(err), or use a small wrapper that adds .catch(next). Express 5 does forward rejected promises automatically, which is a common follow-up question. Exiting the process takes the whole server down for one bad request.

Q15Choose one answermedium

A Node.js API resizes images in JavaScript and every other request slows down while it runs. What is the best fix?

  1. Move the resizing to worker_threads (or a job queue) so it runs off the main thread
  2. Wrap the resize function in async/await
  3. Increase the HTTP server's timeout
  4. Call process.nextTick before resizing
Show answer and explanation

Answer: A. Move the resizing to worker_threads (or a job queue) so it runs off the main thread

Resizing is CPU work, so it keeps the main thread busy and blocks the event loop. Marking a function async does not move CPU work to another thread: it still runs on the main thread. A longer timeout only hides the problem, and nextTick just reorders when the work starts. A worker thread, or a separate worker process fed by a queue, runs the work in parallel and keeps the API responsive.

Q16Choose one answereasy

What does the key prop help React do when rendering a list?

  1. Tell which items changed, were added or were removed between renders
  2. Give each list item a unique style
  3. Make list items focusable with the keyboard
  4. Sort the list alphabetically
Show answer and explanation

Answer: A. Tell which items changed, were added or were removed between renders

Keys give each element a stable identity between renders, so React can match old and new children and then move, insert or remove only the DOM nodes that changed. Without stable keys, React may reuse the wrong component and its state, such as text typed into an input. Use an ID from your data. The array index is only safe when the list never reorders. Keys have nothing to do with styling, focus or sorting.

Q17What does this code output?medium

If count is 0 and this click handler runs once, what is count after the next render?

javascript
function handleClick() {
  setCount(count + 1);
  setCount(count + 1);
  setCount(count + 1);
}
  1. 1
  2. 3
  3. 0
  4. It throws an error
Show answer and explanation

Answer: A. 1

All three calls read the same count from this render, which is 0, so each one asks React to set the value to 1. React batches the updates and renders once, with count equal to 1. To add three, use the updater form setCount(c => c + 1). React passes each updater the latest queued value. This question checks whether you know that state is a snapshot for each render.

Q18Match each item on the leftmedium

Match each React hook to what it is for.

  1. useRef
  2. useMemo
  3. useCallback
  4. useContext
  • Cache a function between renders
  • Keep a mutable value that doesn't cause a re-render
  • Read a value from the nearest provider
  • Cache a computed value between renders
Show answer and explanation

Answer: useRef → Keep a mutable value that doesn't cause a re-renderuseMemo → Cache a computed value between rendersuseCallback → Cache a function between rendersuseContext → Read a value from the nearest provider

useRef holds a mutable .current that survives renders without causing one, which makes it useful for DOM nodes and timer IDs. useMemo caches the result of an expensive calculation, and useCallback caches the function itself, so a child wrapped in React.memo does not re-render because of a new function prop. useContext reads the value from the closest matching provider above the component.

Q19Choose one answerhard

This counter shows 1 and then stops increasing. Why?

javascript
useEffect(() => {
  const id = setInterval(() => setCount(count + 1), 1000);
  return () => clearInterval(id);
}, []);
  1. The interval callback captured count from the first render, so it keeps setting 0 + 1
  2. setInterval does not work inside useEffect
  3. The cleanup function clears the interval after the first tick
  4. React ignores state updates from timers
Show answer and explanation

Answer: A. The interval callback captured count from the first render, so it keeps setting 0 + 1

With an empty dependency array, the effect runs once, and its callback closes over count from the first render, which is 0. Every tick calls setCount(0 + 1), so the value stays at 1. This is called a stale closure. Fix it with the updater form, setCount(c => c + 1), which needs no dependency. The cleanup only runs on unmount, and React handles updates from timers normally.

Q20True or falsemedium

Calling items.push(newItem) and then setItems(items) re-renders the component with the new item.

TrueFalse

Show answer and explanation

Answer: False

False. push changes the existing array, so setItems(items) passes React the same reference it already has. React compares with Object.is, sees no change and skips the re-render, which leaves the UI out of date. Always create a new array: setItems([...items, newItem]), or setItems(prev => [...prev, newItem]). The same rule applies to objects: copy, then change the copy.

Answered them all? Take the same questions against the clock and get a topic-by-topic report.

Start the timed test

More JavaScript quizzes

All JavaScript quizzes →