Skip to main content
DevHivesBlog

Search the blog

Find a post by title, tag or topic

Top JavaScript Interview Questions for 5 Years Experience

At five years of experience, JavaScript interviews test judgment, not recall. Video calls for a park bench is just a Twitter joke, and the only person who can make the call is you.

Arnab Biswas9 min read
WhatsApp
A large question mark, symbolizing the focus on judgment and advanced problem-solving in JS interviews.

Say you have written JavaScript for five years. You can build a feature end to end without asking anyone for help. Then an interviewer asks, "What is a closure?" You give the clean textbook definition, and a rejection email follows two days later. Picture the feedback saying only that your answer sounded like a junior one, with no other detail. Same question, different grading.

At five years of experience, JavaScript interviews test judgment, not recall. You still get closures, the event loop and Promises. But the interviewer now wants to hear the bug each one causes in production, and how you would fix it. A definition alone passes a junior round and fails a senior one.

This list comes from the questions in GreatFrontend's JavaScript interview questions for 5 years experience, checked against MDN where the topic is the runtime itself. For each one, you get the answer that gets you hired.

Closures: from definition to stale-state bugs

A senior answer to "what is a closure?" links it to a real bug, such as a stale value captured inside a React hook. This is the version of the question that experienced candidates are usually asked. A closure is a function that keeps access to the variables around it, even after the outer function has finished running.

The surface answer stops there: "a function that remembers its scope." The deeper answer explains what that memory costs you. In React, every render creates new functions. Each one closes over the values from that render, and keeps them.

Here is the bug interviewers love to show you:

function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1); // count is stuck at its first value
    }, 1000);
    return () => clearInterval(id);
  }, []);
  return <p>{count}</p>;
}

The effect runs once, because the dependency list is empty. Its callback closed over the first count, so the counter goes up by one and then stops. The fix is to stop reading the captured value:

setCount(c => c + 1); // React passes the latest value in

Name the bug ("stale closure"), explain why it happens, then give two fixes. The updater function is one, and it is the one to prefer here, because the interval keeps running without restarting. Adding count to the dependency list is the other, with the trade-off that the interval restarts on every change.

Event loop: how tasks and microtasks give exact output

Microtasks such as Promise callbacks always run before the next task such as setTimeout, because each job runs to completion first, as MDN's page on the JavaScript execution model describes. JavaScript runs your code on a single thread. One job runs and finishes. Only then does the next one start.

Microtasks always finish before the next task starts
Microtasks always finish before the next task starts

Interviewers test this with a "predict the output" snippet. Try this one before you read the answer:

console.log('sync start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => {
  console.log('promise');
  queueMicrotask(() => console.log('nested microtask'));
});
queueMicrotask(() => console.log('queued microtask'));
console.log('sync end');
// Console output, line by line:
// sync start
// sync end
// promise
// queued microtask
// nested microtask
// timeout

Walk through it out loud, the way you would in the room. The two console.log calls are plain synchronous code, so they print first. The .then callback and the first queueMicrotask go into the microtask queue, in that order. The setTimeout callback goes into the task queue.

When the main script finishes, the engine empties the whole microtask queue. That includes the nested microtask, which was added while the queue was draining. Only after that queue is empty does the engine pick up the timeout. MDN calls this rule "run to completion": no job is ever stopped halfway so another can run.

The senior addition is the consequence. A long chain of microtasks can delay rendering and timers, because the browser cannot paint until the queue is empty. InterviewBit calls these timer callbacks macrotasks.

Promises and async code: what to run in parallel

Promise.all only helps when the inputs are independent. With a dependency chain, you await the first step, then run the rest together. The junior answer is "Promise.all is faster." The senior answer asks which calls depend on which.

// The orders and reviews both need the user, so fetch the user first.
const user = await getUser(id);
// These two don't depend on each other, so run them together.
const [orders, reviews] = await Promise.all([
  getOrders(user.accountId),
  getReviews(user.accountId),
]);

Then talk about failure. Promise.all rejects as soon as one input rejects, and you lose the results that did succeed. If a page can still show orders when reviews fail, Promise.allSettled is the better pick. Saying this unprompted is a strong signal.

The follow-up is often about requests you no longer need. Picture a search box: the user types fast, and an older, slower response arrives after a newer one. AbortController cancels the pending fetch when you call controller.abort():

let controller;
async function search(query) {
  controller?.abort();              // cancel the previous request
  controller = new AbortController();
  try {
    const res = await fetch(`/api/search?q=${query}`, {
      signal: controller.signal,
    });
    render(await res.json());
  } catch (err) {
    if (err.name !== 'AbortError') throw err;
  }
}

Two uses are worth naming. One is a user pressing "cancel." The other is making sure only the latest request can update the screen.

What 'this' refers to

The value of this depends on how a function is called, not where it is written. Arrow functions keep the this of the code around them. Most candidates can recite that rule. Fewer can spot where it breaks a real feature.

const timer = {
  seconds: 0,
  tick() {
    this.seconds++;
  },
  start() {
    setTimeout(this.tick, 1000); // bug: tick loses its object
  },
};

Passing this.tick hands over the function alone, without the object. When the timer later calls it, nothing is to the left of the dot, so this is not timer. In strict mode it is undefined, and the line throws.

Give both fixes and say when you would use each:

setTimeout(() => this.tick(), 1000);   // arrow keeps the outer this
setTimeout(this.tick.bind(this), 1000); // bind fixes this for good

The same bug shows up with event handlers and class methods passed as props. Mentioning those places tells the interviewer you have met it in real code.

Junior vs senior grading: the same question, side by side

The same three questions get different marks depending on whether you stop at the definition or go on to the trade-off, a pattern both GreatFrontend and its DEV Community version describe. Use this table as a self-check before any round.

Question Surface answer Deeper answer
What is a closure? A function that remembers its scope Names the stale-closure bug in hooks and gives two fixes with their costs
How do Promises run in parallel? Use Promise.all, it is faster Orders calls by dependency, chooses all or allSettled, cancels stale requests
What does 'this' refer to? The object that owns the method Explains call-site binding and how a callback drops it

Read across each row. The right column always contains a bug and a choice. If your answer has neither, the interviewer has nothing to grade above junior level.

For the event loop, MDN gives you the precise terms ("job," "task queue," "run to completion"). Using them correctly is part of the deeper answer.

Memory leaks in a long-running app

Take heap snapshots in Chrome DevTools and look for Detached HTMLDivElement counts and a JS heap that only rises. A typical scenario, an illustrative example rather than a measured case, is a React app left open all day whose memory climbs toward about 1 GB. A "detached" element is one removed from the page that JavaScript still holds a reference to, so the browser cannot free it.

Describe your process as steps. Interviewers want a method, not a guess:

  1. Take a baseline snapshot

    Open the Memory tab, load the page, and record a heap snapshot.

  2. Repeat the suspect action

    Open and close the same modal or route several times.

  3. Take a second snapshot and compare

    Filter for "Detached" and check whether the count keeps climbing.

  4. Follow the retainers

    Click a detached node and read which closure or listener still points to it.

  5. Fix the cleanup

    Remove the reference in the effect's cleanup function, then measure again.

The usual causes are familiar once you have seen them:

  • A listener added to window or document inside a closure and never removed
  • A setInterval that is never cleared
  • A ResizeObserver or IntersectionObserver that is never disconnected

Notice the link back to the first question. Each of these leaks is a closure holding on to something after the component is gone.

Animation timing: requestAnimationFrame over setInterval

setInterval(updateUI, 16) misses frames and is throttled in background tabs. requestAnimationFrame pauses when the tab is hidden. The 16 in that call is an attempt to match a screen's refresh rate by hand, and it does not hold.

Here is the contrast to give in the interview:

  • setInterval: fires on its own clock. It drifts out of step with the screen, fires while the tab is hidden, and slows down badly on a busy device.
  • requestAnimationFrame: the browser calls your function right before it paints. It matches the display and stops when nobody can see the page, which saves battery.

A strong closing line: "I would also check whether CSS transitions can do this, so JavaScript does not run every frame at all."

Modules: CommonJS vs ES modules

CommonJS uses require and module.exports, while ES modules use import and export, and a senior answer explains when a real project has to mix the two systems.

CommonJS ES modules
require() and module.exports import and export
Loaded while the code runs Read before the code runs, so tools can drop unused exports
The old Node.js default The browser standard, and Node.js with "type": "module"

Mixing happens in real projects. An older internal package may only ship CommonJS while your new service uses ES modules. Explain how you would load it, and why the test runner or bundler might need extra config. That is a judgment answer.

If your role leans toward the server, the Node.js interview questions for 5 years experience post goes deeper on the backend side.

How to prepare for advanced JavaScript interview questions

Practise saying the bug and the fix aloud for each topic, because experienced interviews reward explanation over memorised definitions. Say it the way you would in a live round, because an interviewer can only grade the reasoning they hear while you write the code.

Use this routine for each question:

  1. Write the code

    Type the snippet yourself from memory, without copying it.

  2. Predict the output

    Write down what you expect before you run it.

  3. Explain the bug

    Say what breaks in a real app and why.

  4. State the trade-off

    Give two fixes and the cost of each.

When you run out of material, InterviewBit's JavaScript interview questions has a long list for extra practice. Pair it with something you built: a small project from these JavaScript project ideas gives you real bugs to talk about.

Pick one question from this list today, such as the closure one. Write the code, predict its output, then say the bug and the fix out loud in under two minutes. Do one topic a day for a week.

ShareWhatsAppLinkedInX

Picked from the tags and topic of this post.

A speech bubble holding code with a timer, symbolizing an ML interview problem to solve live.
career growth12 min read

ML Interview Questions for Experienced: 7 Real Problems

Experienced machine learning interviews test production judgment: system design, metrics, and debugging live models. Work through seven real questions that companies in India and the US ask at the 2+ year level, and learn how to answer each one.

Arnab Biswas
Three interview rounds with the first one cleared, showing the stages of the Accenture CSE interview.
career growth10 min read

Accenture Custom Software Engineer Interview Questions (2026)

Accenture's 2026 Custom Software Engineer hiring has three steps: an online assessment, a 45-60 minute technical interview, and an HR round. For the SAP ABAP role, the technical panel tests internal tables, SELECT statements, enhancements and S/4HANA basics.

Arnab Biswas
Two JavaScript logos side by side, representing choosing projects that teach real skills instead of trivial ones.
technology10 min read

10 Beginner JavaScript Project Ideas That Teach Real Skills

A random quote generator taught me more than ten todo apps ever could. These 10 beginner JavaScript projects each build one real skill, from fetch and async/await to game logic and array methods, with full code included.

Arnab Biswas