83 questions - Beginner to advanced

JavaScript interview questions and answers

JavaScript is where most frontend interviews are won or lost. The same topics repeat in almost every company: scope and closures, how this is decided, the event loop, promises, and prototypes. Each answer below is short and uses everyday words.

Last updated:

Types and values

The basics that everything else is built on.

What are the data types in JavaScript?

There are seven primitive types: string, number, boolean, null, undefined, symbol and bigint. There is one non-primitive type: object. Arrays, functions and dates are all objects underneath.

What is the difference between null and undefined?

undefined means a value was never set. You declared a variable but did not give it a value, or a function argument is missing. null means you set it to "no value" on purpose. JavaScript gives you undefined. You give null.

let a;
console.log(a);        // undefined
let b = null;
console.log(b);        // null
console.log(typeof a); // "undefined"
console.log(typeof b); // "object"  (a very old bug in the language)

What is the difference between == and ===?

=== checks value and type, with no conversion. == changes the types first, which gives strange results. Always use === unless you want null and undefined to be treated as equal.

0 == "0"        // true
0 === "0"       // false
null == undefined   // true
null === undefined  // false
NaN === NaN     // false - use Number.isNaN()

What values are falsy in JavaScript?

Exactly eight: false, 0, -0, 0n, "" (empty string), null, undefined and NaN. Everything else is truthy, including "0", "false", [] and {}.

What is NaN?

NaN means Not a Number. You get it when a maths operation cannot give a real number, like Number("abc") or 0/0. Its type is still "number". NaN is not equal to anything, not even itself, so use Number.isNaN() to test it.

Number("abc")        // NaN
NaN === NaN          // false
Number.isNaN(NaN)    // true

What is the difference between primitive and reference types?

Primitives are copied by value. Changing the copy does not touch the original. Objects and arrays are copied by reference. Both names point to the same thing in memory, so a change through one name is seen through the other.

let a = 1, b = a;  b = 2;
console.log(a); // 1

let x = { n: 1 }, y = x;  y.n = 2;
console.log(x.n); // 2

How do you make a shallow copy and a deep copy?

A shallow copy copies only the top level, so nested objects are still shared. Use spread or Object.assign. A deep copy copies everything. Use structuredClone, which also handles dates, maps and loops in the data.

const shallow = { ...original };
const deep = structuredClone(original);
// JSON.parse(JSON.stringify(x)) also works but loses dates, undefined and functions

Why is 0.1 + 0.2 not equal to 0.3?

Numbers are stored as 64-bit floating point in binary. 0.1 and 0.2 cannot be stored exactly, so a very small error is left behind. Compare with a small tolerance, or work in the smallest unit. Store money in paise, not rupees.

0.1 + 0.2                       // 0.30000000000000004
Math.abs(0.1 + 0.2 - 0.3) < 1e-9 // true

What does typeof return for an array?

"object", which does not help. Use Array.isArray(value) to check for an array.

What is type coercion?

It is JavaScript changing one type into another on its own. For example it turns a number into a string when you use + with a string. That is why "5" + 1 gives "51" but "5" - 1 gives 4, because - has no string meaning.

What is a Symbol?

A Symbol is a unique value used as an object key. It can never clash with another key, even one with the same description. Libraries use symbols to add hidden behaviour to objects without breaking normal loops.

What is the difference between a Map and a plain object?

A Map accepts any type as a key, including objects. It keeps the insertion order, has a size property, and is faster when you add and delete a lot. A plain object only takes string and symbol keys and also inherits properties from its prototype.

What is a Set?

A Set is a collection of unique values. Adding a value that is already there does nothing. That makes removing duplicates from an array a one-liner.

const unique = [...new Set([1, 2, 2, 3])]; // [1, 2, 3]

Scope, hoisting and closures

The most common source of interview questions.

What is the difference between var, let and const?

var is function scoped, can be declared again, and is hoisted as undefined. let and const are block scoped, cannot be declared again in the same scope, and cannot be used before their line runs. const cannot be given a new value, but the contents of a const object can still change.

if (true) {
  var a = 1;
  let b = 2;
}
console.log(a); // 1
console.log(b); // ReferenceError

What is hoisting?

Before running your code, JavaScript moves declarations to the top of their scope. Function declarations move fully, so you can call them before they appear. var moves too, but its value is undefined until the line runs. let and const also move, but you cannot use them before their line.

What is the temporal dead zone?

It is the gap between the start of a block and the line where a let or const is declared. Reading the variable in that gap throws a ReferenceError. It exists to catch bugs that var would hide silently.

What is a closure?

A closure is a function that remembers the variables from the place where it was created, even after the outer function has finished. It is how JavaScript keeps data private.

function counter() {
  let count = 0;               // private
  return function () {
    count += 1;
    return count;
  };
}
const next = counter();
next(); // 1
next(); // 2

Give a real use of closures.

Keeping data private inside a module. Remembering settings in a factory function. Helpers like debounce and throttle that must keep a timer id between calls. React hooks are also built on closures.

Why does a for loop with var print the same number in setTimeout?

Because var has one single variable shared by every round of the loop. By the time the timers run, the loop is over and they all read the final value. let makes a fresh variable for each round, which fixes it.

for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)); // 3 3 3
for (let i = 0; i < 3; i++) setTimeout(() => console.log(i)); // 0 1 2

What is lexical scope?

It means the scope of a variable is decided by where you wrote the code, not by where the function is called from. A function inside another function can read the outer variables.

What is an IIFE?

IIFE means Immediately Invoked Function Expression. It is a function that runs the moment it is written. Before modules existed, it was the normal way to keep variables off the global object.

(function () {
  const secret = 42; // not visible outside
})();

What is the difference between a function declaration and a function expression?

A declaration starts with the function keyword and is hoisted fully, so you can call it earlier in the file. An expression is stored in a variable and exists only after that line runs.

this, functions and objects

How is the value of this decided?

By how the function is called, not by where it is written. Called as a method, this is the object before the dot. Called on its own in strict mode, this is undefined. With new, this is the new object. With call, apply or bind, this is what you passed. An arrow function has no this of its own and uses the one around it.

What is the difference between an arrow function and a normal function?

An arrow function has no this of its own, no arguments object, cannot be used with new, and has no prototype. That makes it perfect for callbacks and wrong for object methods and constructors.

const obj = {
  name: "Asha",
  normal() { return this.name; },      // "Asha"
  arrow: () => this.name,               // undefined
};

What is a callback function?

A callback is a function you pass into another function, to be called later. Array methods take callbacks. So do event listeners and timers. The other function decides when to call it.

setTimeout(() => console.log("later"), 1000);
[1, 2, 3].map((n) => n * 2);

What is a higher-order function?

A higher-order function is a function that takes a function as an argument, or returns a function, or both. map, filter and reduce are higher-order functions. So is a function that returns a debounced version of another function.

function once(fn) {          // takes a function, returns a function
  let done = false;
  return (...args) => {
    if (done) return;
    done = true;
    return fn(...args);
  };
}

What is the difference between call, apply and bind?

call and apply both run the function at once with a this you choose. call takes arguments one by one. apply takes them as an array. bind does not run the function. It returns a new function with this fixed.

function greet(city) { return this.name + " from " + city; }
const user = { name: "Ravi" };
greet.call(user, "Pune");
greet.apply(user, ["Pune"]);
const bound = greet.bind(user);
bound("Pune");

What is the prototype chain?

Every object has a hidden link to another object called its prototype. When you read a property, JavaScript looks on the object, then on its prototype, then on that one, until it finds the property or reaches null. That chain is how inheritance works in JavaScript.

What is the difference between __proto__ and prototype?

prototype is a property on a constructor function. It becomes the prototype of objects made with new. __proto__ is the real link on an instance that points to its prototype object. In real code use Object.getPrototypeOf() instead of __proto__.

Are JavaScript classes real classes?

No. They are a nicer way to write prototypes. A class body is easier to read, gives real private fields with #, and forces you to use new. Underneath it still builds a prototype chain.

class Animal {
  #id = 1;                    // private field
  constructor(name) { this.name = name; }
  speak() { return this.name + " makes a sound"; }
}
class Dog extends Animal {
  speak() { return super.speak() + " (woof)"; }
}

What does the new keyword actually do?

Four steps. It makes an empty object. It links that object to the prototype of the constructor. It runs the constructor with this pointing to the new object. It returns that object, unless the constructor returns another object.

What is the difference between Object.freeze and const?

const stops you from giving the variable a new value. Object.freeze stops you from changing the properties inside the object. freeze is also shallow, so nested objects can still change.

What is optional chaining and nullish coalescing?

?. reads a nested property and gives undefined instead of an error when something in the middle is null or undefined. ?? gives a fallback only when the left side is null or undefined. || also fires on 0 and on an empty string, which hides bugs.

const city = user?.address?.city;
const count = input ?? 0;   // 0 stays 0
const wrong = input || 0;   // 0 becomes 0 even when input is 0 - hides bugs

What is destructuring?

It is a short way to pull values out of an array or object into variables. It also supports default values and renaming.

const { name, age = 18, address: { city } = {} } = user;
const [first, ...rest] = list;

What is the difference between rest and spread?

They look the same but do the opposite. Rest collects many values into one array or object, usually in a parameter list. Spread opens one array or object out into separate values.

function sum(...nums) { return nums.reduce((a, b) => a + b, 0); } // rest
const merged = { ...a, ...b };                                     // spread

What is currying?

Currying turns a function with many arguments into a chain of functions that each take one. You can then fix some arguments early and reuse the result.

const add = (a) => (b) => a + b;
const add5 = add(5);
add5(3); // 8

What is a pure function?

A pure function gives the same output for the same input and changes nothing outside itself. No network calls, no changing its arguments, no writing to globals. Pure functions are easy to test and safe to reuse.

Arrays and objects in practice

What is the difference between map, forEach and filter?

map returns a new array of the same length with every item changed. filter returns a new, shorter array with only the items that pass a test. forEach returns undefined and is only for side effects.

How does reduce work?

reduce walks the array and builds one result. It takes a callback with a running total and the current item, plus a starting value. Always pass the starting value, because an empty array without it throws an error.

const total = items.reduce((sum, item) => sum + item.price, 0);

const byRole = users.reduce((acc, u) => {
  (acc[u.role] ||= []).push(u);
  return acc;
}, {});

What is the difference between slice and splice?

slice returns a copy of a part of the array and leaves the original alone. splice changes the original array by removing or adding items, and returns what it removed.

What is the difference between find and filter?

find returns the first matching item itself, or undefined. filter returns an array of every matching item, which can be empty.

How do you sort an array of numbers correctly?

sort turns items into strings by default, so [10, 9] stays [10, 9]. Pass a compare function. sort also changes the original array, so copy it first if that matters. toSorted() returns a new array instead.

const asc = [...nums].sort((a, b) => a - b);
const byName = [...users].sort((a, b) => a.name.localeCompare(b.name));

How do you flatten a nested array?

Use flat with a depth, or flat(Infinity) for any depth. flatMap does a map and a one-level flat in one step.

[1, [2, [3, [4]]]].flat(2);   // [1, 2, 3, [4]]
[1, [2, [3, [4]]]].flat(Infinity);

What is the difference between for...in and for...of?

for...in loops over the keys of an object, including inherited ones, so it fits arrays badly. for...of loops over the values of anything iterable: arrays, strings, maps and sets.

How do you remove duplicate objects from an array by id?

Build a Map with the id as the key and take its values. It keeps the last one for each id and stays O(n).

const unique = [...new Map(list.map((x) => [x.id, x])).values()];

What do Object.keys, Object.values and Object.entries return?

They return arrays: the own keys, the own values, and [key, value] pairs. Object.fromEntries turns a pairs array back into an object, which works well after a filter or a map.

const cleaned = Object.fromEntries(
  Object.entries(obj).filter(([, v]) => v != null)
);

What is the difference between mutating and non-mutating array methods?

push, pop, splice, sort and reverse change the original array. map, filter, slice, concat and the newer toSorted, toReversed and with return a new array. In React and Redux you must use the non-mutating ones.

Event loop and asynchronous JavaScript

Expect at least two questions from this section.

Is JavaScript single threaded?

Yes. It runs your code on one thread with one call stack. It still feels like many things happen together, because timers, network calls and file work are handled outside that thread by the browser or Node. The results come back as callbacks.

Explain the event loop.

The call stack runs your code. When the stack is empty, the event loop first empties the microtask queue, which holds promise callbacks and queueMicrotask. Only then it takes one task from the macrotask queue, like a setTimeout callback or a click handler. Microtasks always run before the next macrotask.

What is the output of this classic ordering question?

Synchronous code runs first. Then microtasks, which are promises. Then macrotasks, which are timers. So the order is 1, 4, 3, 2.

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

What is the difference between a microtask and a macrotask?

Microtasks are promise callbacks, queueMicrotask and MutationObserver. Macrotasks are setTimeout, setInterval, input and output work, and UI events. The whole microtask queue is emptied after each macrotask, so a promise chain that never ends can block drawing.

Does setTimeout(fn, 0) run immediately?

No. It puts the callback in the macrotask queue, so it runs after the current code and after all waiting microtasks. Browsers also push nested timers to about 4ms.

What is the difference between setTimeout and setInterval?

setTimeout runs the callback once, after the delay. setInterval runs it again and again with that gap. setInterval does not wait for slow work to finish, so calls can pile up. A safer pattern is setTimeout that schedules itself again at the end.

function poll() {
  doWork();
  setTimeout(poll, 5000);   // next timer starts only after doWork finishes
}
poll();

What is a Promise?

A Promise is an object for a value that is not ready yet. It starts as pending and then settles once: fulfilled with a value, or rejected with an error. You react to it with then, catch and finally.

What is callback hell and how do promises fix it?

Callback hell is callbacks inside callbacks, each step pushed further to the right. Errors become very hard to handle. Promises make that a flat chain, and async/await makes it read like normal step-by-step code.

What is the difference between Promise.all, allSettled, race and any?

all waits for all of them and fails at once if any one fails. allSettled waits for all and never fails, giving you the status of each. race settles with whichever finishes first, success or failure. any resolves with the first success and fails only if every one fails.

const [user, orders] = await Promise.all([getUser(), getOrders()]);
const results = await Promise.allSettled(tasks);

What is async/await?

It is a nicer way to write promises. An async function always returns a promise. await pauses inside that function until the promise settles, without blocking the main thread. Handle failures with try/catch.

async function loadUser(id) {
  try {
    const res = await fetch("/api/users/" + id);
    if (!res.ok) throw new Error("HTTP " + res.status);
    return await res.json();
  } catch (err) {
    console.error(err);
    return null;
  }
}

What is a common mistake with await inside a loop?

await inside a for loop makes the requests run one after another. That is slow when they do not depend on each other. Start them all and wait together with Promise.all. Keep the one-by-one version only when each step needs the result of the last one.

// slow
for (const id of ids) { await fetchUser(id); }

// fast
await Promise.all(ids.map(fetchUser));

How do you cancel a fetch request?

Use an AbortController. Pass its signal to fetch, then call abort() when the user leaves the page or types a new search term.

const controller = new AbortController();
fetch(url, { signal: controller.signal });
controller.abort();

What is an unhandled promise rejection?

It is a rejected promise with no catch on it. The browser logs an error. In Node it can stop the process. Always add a catch, or put the await inside try/catch.

What is a generator function?

A generator is written with function* and can pause at each yield and start again later. Calling it returns an iterator. Generators are used for lazy sequences and by libraries like redux-saga.

function* ids() {
  let i = 1;
  while (true) yield i++;
}
const gen = ids();
gen.next().value; // 1

DOM and browser

What is the DOM?

DOM means Document Object Model. It is the tree of objects the browser builds from your HTML. JavaScript reads and changes that tree, and the browser redraws the page to match.

What is the difference between querySelector and getElementById?

getElementById takes only an id and is slightly faster. querySelector takes any CSS selector and returns the first match. querySelectorAll returns a static NodeList of all matches.

What is event bubbling and capturing?

When you click an element, the event first travels down from the document to that element, which is capturing. Then it travels back up to the document, which is bubbling. Handlers run during bubbling by default. Pass true or { capture: true } to run during capturing.

What is event delegation?

Instead of a listener on every child, you put one listener on the parent and check event.target. It uses less memory and also works for elements added later.

list.addEventListener("click", (e) => {
  const item = e.target.closest("li");
  if (item) console.log(item.dataset.id);
});

What is the difference between stopPropagation and preventDefault?

preventDefault stops the default browser action, like following a link or sending a form. stopPropagation stops the event from moving on to parent handlers. They do different jobs, and sometimes you need both.

What is the difference between the target and currentTarget of an event?

target is the element that was really clicked. currentTarget is the element whose listener is running now. In event delegation they are usually different.

What is debouncing and throttling?

Debounce waits until the user has stopped for a set time and then runs once. It suits a search box. Throttle runs at most once in each interval, no matter how often the event fires. It suits scroll and resize.

function debounce(fn, wait) {
  let t;
  return (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), wait);
  };
}

What is the difference between innerHTML, innerText and textContent?

innerHTML reads and writes HTML, and it is a security risk with user input. textContent reads all the text, even from hidden elements, and is fast. innerText reads only what is visible and forces a layout, so it is slower.

What is CORS?

CORS means Cross-Origin Resource Sharing. By default the browser stops a page on one origin from reading a response from another origin. The server must send Access-Control-Allow-Origin headers to allow it. It is fixed on the server, not in your JavaScript.

What is XSS and how do you prevent it?

XSS means Cross-Site Scripting. It happens when text controlled by an attacker runs as script on your page. Prevent it: never put raw user input into innerHTML, escape output, use textContent, and add a Content Security Policy.

What is the difference between an ES module and CommonJS?

ES modules use import and export. They are read before running, so bundlers can drop unused code, and they support top-level await. CommonJS uses require and module.exports, is loaded while running, and is the older Node format.

What is the IntersectionObserver?

It is a browser API that tells you when an element enters or leaves the screen, without listening to scroll events. It is the right way to build lazy loading, infinite scroll and scroll animations.

Tricky and output-based questions

The short puzzles interviewers use to check depth.

What does [] + {} and {} + [] give?

[] + {} gives the string "[object Object]", because both sides are turned into strings. In a console, {} + [] can give 0, because the {} at the start is read as an empty block, not an object, and then +[] is 0.

Why does typeof null return "object"?

It is a bug from the first version of JavaScript. The type tag for objects was 0, and null was stored as an all-zero pointer. It was never fixed because too much code now depends on it.

What is the difference between shallow and deep equality of objects?

Two objects are === only when they are the same object in memory. So { a: 1 } === { a: 1 } is false. To compare the contents you must check them yourself or use a helper.

What does "use strict" change?

It turns silent mistakes into errors. You cannot create a global by accident. this is undefined in a plain function call. Duplicate parameter names are not allowed. ES modules and class bodies are always strict.

What is memoization?

Memoization saves the result of a function against its arguments, so the same call again is instant. It works only for pure functions and it uses extra memory.

function memoize(fn) {
  const cache = new Map();
  return (n) => {
    if (cache.has(n)) return cache.get(n);
    const value = fn(n);
    cache.set(n, value);
    return value;
  };
}

What is a memory leak in JavaScript and what causes it?

It is memory you no longer need but cannot free, because something still points to it. Common causes: a setInterval you forgot to clear, listeners never removed, global caches that keep growing, and closures holding big objects. WeakMap and WeakRef help, because they do not block garbage collection.

What is the difference between synchronous and asynchronous code?

Synchronous code runs line by line, and each line waits for the one before it. Asynchronous code starts a job and moves on, then handles the result later through a callback, a promise or await.

What is the difference between call stack and heap?

The call stack tracks the function running now and everything that called it. It holds primitives and references. The heap is the large, unordered memory area where objects live.

What is tree shaking?

Tree shaking is a bundler feature that removes exported code nobody imports. It needs static ES module imports, which is one reason to avoid dynamic require in frontend code.

What is the difference between localStorage and a cookie for a token?

localStorage can be read by any script on the page, so one XSS bug leaks the token. An httpOnly cookie cannot be read by JavaScript, but it is sent on its own, so it needs CSRF protection. Most teams pick the httpOnly cookie with a SameSite setting.

Continue with another topic

Practise live

CrackInterviewAI listens to the live interview and gives a structured answer on screen for coding, system design, HR and project questions. Download for Windows.