79 questions - Beginner to advanced

JavaScript interview questions and answers

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

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 - and 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 has never been set - a variable you declared but did not assign, or a missing function argument. null means you deliberately set it to "no value". undefined is given by JavaScript, null is given by you.

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 ===?

=== compares value and type with no conversion. == converts the types first, which produces surprising results. Always use === unless you deliberately want to treat null and undefined 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 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 at the same thing in memory, so a change through one name is visible 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 cycles.

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, and 0.1 and 0.2 cannot be represented exactly, so a tiny error remains. 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 is not helpful. Use Array.isArray(value) to check for an array.

What is type coercion?

It is JavaScript automatically converting one type into another - for example turning a number into a string when you use + with a string. It is the reason "5" + 1 gives "51" but "5" - 1 gives 4, because - has no string meaning.

What is a Symbol?

A unique value used as an object key that 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, remembers insertion order, has a size property and is faster for frequent adding and deleting. A plain object only supports string and symbol keys and inherits properties from its prototype.

What is a Set?

A collection of unique values. Adding a value that already exists does nothing, which 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 and can be redeclared, and it is hoisted as undefined. let and const are block scoped, cannot be redeclared in the same scope, and sit in the temporal dead zone until the line runs. const cannot be reassigned, though 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 code, JavaScript moves declarations to the top of their scope. Function declarations are fully hoisted so you can call them before they appear. var is hoisted but set to undefined. let and const are hoisted too, but you cannot touch them before the declaration line.

What is the temporal dead zone?

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 silently hide.

What is a closure?

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

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.

Data privacy in a module, remembering configuration in a factory function, and helpers like debounce and throttle that need to 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 binding shared by every iteration, so by the time the timers run, the loop has finished and all of them read the final value. let creates a fresh binding per iteration, 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 the code is written, not by where the function is called from. Nested functions can read the variables of the functions around them.

What is an IIFE?

An Immediately Invoked Function Expression - a function that runs the moment it is defined. Before modules existed, it was the standard way to avoid putting variables on 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 fully hoisted, so it can be called earlier in the file. An expression is assigned to a variable and only exists after that line runs.

this, functions and objects

How is the value of this decided?

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

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

An arrow function has no own this, 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 the difference between call, apply and bind?

call and apply both run the function immediately with a chosen this; call takes arguments one by one and apply takes them as an array. bind does not run the function - it returns a new function with this locked in.

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 prototype, until it finds it 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 created with new. __proto__ is the actual link on an instance pointing to its prototype object. Use Object.getPrototypeOf() instead of __proto__ in real code.

Are JavaScript classes real classes?

No, they are syntax sugar over prototypes. A class body is easier to read, adds real private fields with #, and forces you to use new, but 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 creates an empty object, links that object prototype to the constructor prototype, runs the constructor with this pointing at the new object, and returns the object unless the constructor returns another object.

What is the difference between Object.freeze and const?

const stops you from reassigning the variable name. Object.freeze stops you from changing the properties inside the object. Freeze is also shallow - nested objects can still be changed.

What is optional chaining and nullish coalescing?

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

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?

A short way to pull values out of an array or object into variables, with support for defaults 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 opposite things. Rest collects several values into one array or object, usually in a parameter list. Spread expands one array or object into separate values.

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

What is currying?

Turning a function with several arguments into a chain of functions that each take one. It lets you 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 function that returns the same output for the same input and changes nothing outside itself - no network calls, no mutation of 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 each item transformed. 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 a single result. It takes a callback with an accumulator and the current item, plus a starting value. Always pass the starting value, otherwise an empty array throws.

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 inserting 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 all matching items, which may be empty.

How do you sort an array of numbers correctly?

sort converts items to strings by default, so [10, 9] sorts as [10, 9]. Pass a compare function. Also note that sort changes the original array, so copy it first if that matters. toSorted() returns a new array.

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 together.

[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 is a poor fit for arrays. for...of loops over the values of anything iterable - arrays, strings, maps, sets.

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

Build a Map keyed by id and take the values, which keeps the last occurrence 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?

Arrays of the object own enumerable keys, values, and [key, value] pairs. Object.fromEntries turns a pairs array back into an object, which pairs nicely with 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 concurrent because timers, network requests and file work are handled by the browser or Node outside that thread, and the results come back as callbacks.

Explain the event loop.

The call stack runs your code. When it is empty, the event loop first empties the microtask queue - promise callbacks and queueMicrotask - and only then takes one task from the macrotask queue, such as 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 first, then microtasks (promises), then macrotasks (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, I/O and UI events. The whole microtask queue is drained after each macrotask, so a promise chain that never ends can block rendering.

Does setTimeout(fn, 0) run immediately?

No. It schedules the callback as a macrotask, so it runs after the current code and after all pending microtasks. Browsers also clamp nested timers to about 4ms.

What is a Promise?

An object that represents a value that is not ready yet. It is pending at first and then settles once, either fulfilled with a value or rejected with an error. You react with then, catch and finally.

What is callback hell and how do promises fix it?

Callback hell is deeply nested callbacks where each step is indented inside the last, making errors hard to handle. Promises flatten that into a chain, and async/await makes it look like normal sequential code.

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

all waits for every promise and rejects immediately if any one fails. allSettled waits for all and always succeeds, giving you the status of each. race settles with whichever finishes first, success or failure. any resolves with the first success and only rejects if all fail.

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

What is async/await?

Syntax sugar over promises. An async function always returns a promise, and 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?

Awaiting inside a for loop makes the requests run one after another, which is slow when they are independent. Start them all and await together with Promise.all. Keep the sequential version only when each step needs the previous result.

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

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

How do you cancel a fetch request?

With an AbortController. You pass its signal to fetch and call abort() when the user navigates away or types a new search term.

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

What is an unhandled promise rejection?

A rejected promise with no catch attached. In the browser it logs an error, and in Node it can crash the process. Always attach a catch or wrap the await in try/catch.

What is a generator function?

A function written with function* that can pause at each yield and resume 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?

The Document Object Model is the browser tree of objects built 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, and 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 the target (capturing), then back up to the document (bubbling). Handlers run in the bubbling phase by default; pass true or { capture: true } to run during capturing.

What is event delegation?

Instead of adding a listener to every child, you add one listener on the parent and check event.target. It uses less memory and 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 browser default action, like following a link or submitting a form. stopPropagation stops the event from travelling further to parent handlers. They are unrelated and you sometimes need both.

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

target is the element that was actually clicked. currentTarget is the element whose listener is running. 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 - good for a search box. Throttle runs at most once per interval no matter how often the event fires - good for 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 or writes HTML and is a security risk with user input. textContent reads all the text including hidden elements and is fast. innerText reads only what is visible and triggers a reflow, so it is slower.

What is CORS?

Cross-Origin Resource Sharing. By default a browser blocks a page on one origin from reading a response from another origin. The server must send Access-Control-Allow-Origin headers to permit it. It is a server-side fix, not something you can turn off in your JavaScript.

What is XSS and how do you prevent it?

Cross-Site Scripting is when attacker-controlled text is executed as script on your page. Prevent it by never inserting raw user input with innerHTML, escaping output, using textContent, and adding a Content Security Policy.

What is the difference between an ES module and CommonJS?

ES modules use import and export, are loaded statically so bundlers can tree-shake them, and support top-level await. CommonJS uses require and module.exports, is loaded at runtime and is the older Node format.

What is the IntersectionObserver?

A browser API that tells you when an element enters or leaves the viewport, 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 are converted to strings. In a console, {} + [] can give 0 because the leading {} 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 very first version of JavaScript, where the type tag for objects was 0 and null was represented as an all-zero pointer. It was never fixed because too much code depends on it.

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

Two objects are only === when they are literally the same object in memory. { a: 1 } === { a: 1 } is false. Compare the contents yourself, or use a helper.

What does "use strict" change?

It turns silent mistakes into errors: you cannot create an undeclared global, this is undefined in a plain function call, and duplicate parameter names are illegal. ES modules and class bodies are always strict.

What is memoization?

Caching the result of a function against its arguments so repeated calls are instant. It works only for pure functions and costs 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?

Memory that is no longer needed but cannot be freed because something still references it. Common causes: forgotten setInterval, listeners never removed, growing global caches, and closures holding large objects. WeakMap and WeakRef help because they do not stop garbage collection.

What is the difference between synchronous and asynchronous code?

Synchronous code runs line by line and each line waits for the previous one. Asynchronous code starts something and continues, then handles the result later through a callback, promise or await.

What is the difference between call stack and heap?

The call stack keeps track of the function currently running and everything that called it; it holds primitives and references. The heap is the larger unordered memory area where objects live.

What is tree shaking?

A bundler feature that removes exported code nobody imports. It works with 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 is readable by any script on the page, so an XSS bug leaks the token. An httpOnly cookie cannot be read by JavaScript but is sent automatically, so it needs CSRF protection. Most teams choose the httpOnly cookie plus 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.