JavaScript Interview Questions, Actually Explained

  • Career
  • Published
  • Updated
  • 4 min read
JavaScript Interview Questions, Actually Explained

Interviewers ask these because the answers reveal whether you understand how JavaScript works or have memorised a list. The follow-up question is always where people come apart, so each answer below includes the follow-up.

What is a closure?

A function that keeps access to the variables from the scope it was created in, even after that outer function has finished running.

function counter() {
  let count = 0;             // lives in counter's scope
  return () => ++count;      // this function closes over count
}

const next = counter();
next(); // 1
next(); // 2  <- count survived, because next still references it

Follow-up: why does this matter? Because it is how private state works in JavaScript, how React hooks hold values between renders, and it is the cause of the classic loop bug where var makes every callback see the final value.

Explain the event loop

JavaScript runs on one thread. The call stack executes your code. When you call something asynchronous, the work is handed off — to the browser or to Node — and a callback is queued. When the stack is empty, the event loop moves queued callbacks onto it.

The part that separates real understanding from recitation: there are two queues. Microtasks (promise callbacks) drain completely before the next macrotask (setTimeout, I/O).

console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');

// 1, 4, 3, 2

The promise wins over the zero-millisecond timeout because microtasks are drained first. If you can explain that ordering, you have answered the question properly.

What is "this"?

In a normal function, this is determined by how the function is called, not where it is defined. Four cases:

  • Called as a method — this is the object before the dot
  • Called plainly — this is undefined in strict mode, the global object otherwise
  • Called with call, apply or bind — this is whatever you passed
  • Called with new — this is the newly created object

Arrow functions are the exception: they have no this of their own and inherit it from the enclosing scope. That is precisely why they fixed the old pattern of writing const self = this.

var, let and const

  • var — function-scoped, hoisted and initialised as undefined, can be redeclared
  • let — block-scoped, hoisted but in the temporal dead zone until assigned
  • const — same as let, but the binding cannot be reassigned

The follow-up: does const make an object immutable? No. It prevents reassigning the variable. The object contents can still be mutated. Use Object.freeze if you need shallow immutability.

Explain the prototype chain

Every object has a hidden link to another object, its prototype. When you access a property that does not exist on the object, JavaScript follows that link upward until it finds it or reaches null.

This is how inheritance works, and class syntax is a nicer way to write the same mechanism rather than a different one.

== versus ===

=== compares value and type. == coerces types before comparing, using rules that surprise people.

0 == '0'      // true
0 == []       // true
'0' == []     // false  <- these three cannot all be consistent
null == undefined  // true
null === undefined // false

Use === always. The one accepted exception is x == null to check for null or undefined together.

Debounce versus throttle

  • Debounce — wait until activity stops, then run once. Right for search-as-you-type.
  • Throttle — run at most once per interval regardless of activity. Right for scroll and resize handlers.
function debounce(fn, delay) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

Being asked to write debounce from memory is common. It is also a closure question in disguise.

Shallow versus deep copy

Spread and Object.assign copy one level. Nested objects are still shared references, which is the source of an enormous number of state bugs in React.

const a = { user: { name: 'Ali' } };
const b = { ...a };
b.user.name = 'Sara';
a.user.name; // 'Sara' - same nested object

const deep = structuredClone(a); // properly independent

How to prepare

Do not memorise these answers. Write the code, break it deliberately, and see what happens. An interviewer can tell the difference within one follow-up question, and the follow-up is what the interview is actually about.

Need help building this?

I take on web app, mobile and e-commerce projects. Tell me what you are building and I will reply within 24 hours with scope, timeline and a fixed quote.

Start a project