Skip to content
Interview questions

JavaScript interview questions

Learn with visible answers or test yourself before revealing each explanation.

Choose your mode

01

What is JavaScript?

JavaScript is a high-level, dynamically typed programming language. It is used in browsers to add behavior to web pages and also runs outside browsers in environments such as Node.js. Modern engines usually compile JavaScript at runtime instead of only interpreting it line by line.

02

What is the difference between let, const, and var?

let and const are block-scoped. let can be reassigned, while const cannot be reassigned and must be initialized when declared. A const object can still have its properties changed. var is function-scoped, can be redeclared in the same scope, and is initialized with undefined during hoisting.

let score = 10;
score = 11;

const user = { name: "Asha" };
user.name = "Ravi";

var active = true;

Use const by default, and use let when the variable must be reassigned. Avoid var in modern application code unless its behavior is intentional.

03

What is hoisting, and what is the temporal dead zone?

Before executing a scope, JavaScript creates bindings for its declarations. Function declarations can be called before their source position. var exists and contains undefined before its declaration runs. let and const also exist, but accessing them before initialization throws a ReferenceError. That period is called the temporal dead zone.

sayHello(); // Works
function sayHello() {
  console.log("Hello");
}

console.log(total); // undefined
var total = 3;

console.log(name); // ReferenceError
let name = "Mira";
04

What is lexical scope?

Lexical scope means variable access is determined by where functions and blocks are written in the source code. An inner function can read bindings from its own scope and its outer scopes, but an outer function cannot read bindings declared only inside the inner function.

const label = "outer";

function showLabel() {
  console.log(label);
}

showLabel(); // outer
05

How do global, function, and block scope differ?

A global binding is available throughout its script or module. A function-scoped binding is available only inside that function. A block-scoped binding declared with let, const, or class is limited to its nearest block, such as an if statement or loop.

function example() {
  var functionValue = 1;
  if (true) {
    const blockValue = 2;
  }
  console.log(functionValue);
  // blockValue is not available here
}
06

What is variable shadowing?

Shadowing happens when an inner scope declares a binding with the same name as one in an outer scope. References inside the inner scope use the inner binding, while the outer binding remains unchanged.

const status = "global";

function check() {
  const status = "local";
  console.log(status); // local
}
07

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

The == operator performs type coercion before comparing many values, which can produce surprising results. The === operator compares without coercing the operands, so both type and value must match. Prefer === unless loose equality behavior is specifically required.

0 == false;  // true
0 === false; // false

null == undefined;  // true
null === undefined; // false
08

What is the difference between null and undefined?

undefined usually means a value has not been assigned or a property does not exist. null is an explicit value commonly used to represent an intentional absence. They are different primitive values, although loose equality treats them as equal.

09

Which JavaScript values are mutable and immutable?

Primitive values such as strings, numbers, booleans, bigint, symbols, null, and undefined are immutable. Objects, including arrays and functions, are mutable. Reassigning a variable is different from mutating the object stored in that variable.

const items = [1, 2];
items.push(3); // The array changes

let title = "Hi";
title = title + "!"; // A new string value is assigned
10

How do arrow functions differ from regular functions?

Arrow functions use shorter syntax and capture this from the surrounding scope. They do not have their own arguments object, cannot be used with new, and do not have a prototype property for construction. Regular functions receive this from how they are called and can be constructors when constructable.

const add = (a, b) => a + b;

const counter = {
  value: 1,
  read() {
    const getValue = () => this.value;
    return getValue();
  },
};
11

What is the difference between spread and rest syntax?

Both use three dots, but their jobs depend on position. Spread expands an iterable into arguments or array elements, or copies enumerable own properties into an object. Rest collects remaining function arguments, array elements, or object properties into a new value.

const values = [2, 3];
const all = [1, ...values];

function sum(...numbers) {
  return numbers.reduce((total, value) => total + value, 0);
}

Array and object spread create shallow copies, not deep copies.

12

What is destructuring assignment?

Destructuring extracts array elements or object properties into variables. It supports default values, renamed bindings, nested patterns, and rest elements.

const [first, second = 0] = [10];
const { name: displayName, ...details } = {
  name: "Nila",
  role: "Developer",
};
13

How do map(), filter(), and reduce() differ?

map() creates a new array by transforming every element. filter() creates a new array containing only elements that pass a test. reduce() combines the elements into one accumulated result, which can be a number, object, array, or another value.

const values = [1, 2, 3];

values.map((value) => value * 2);       // [2, 4, 6]
values.filter((value) => value > 1);    // [2, 3]
values.reduce((sum, value) => sum + value, 0); // 6
14

What is the difference between synchronous and asynchronous code?

Synchronous code completes one operation before moving to the next statement. Asynchronous operations can finish later, allowing JavaScript to continue other work in the meantime. Asynchronous does not automatically mean parallel; the host environment and runtime decide where the underlying work happens.

15

What are an execution context and the call stack?

An execution context stores the information needed to run global code or a function, including its bindings, outer scope reference, and this value. The call stack tracks active execution contexts. Calling a function pushes a context onto the stack, and returning removes it.

16

How does the JavaScript event loop work?

JavaScript runs synchronous work on the call stack. The host queues callbacks when timers, network operations, or events are ready. After the current stack is empty, the event loop lets queued work run. Promise reactions use the microtask queue, which is drained before the next regular task such as a timer callback.

console.log("A");
setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => console.log("C"));
console.log("D");

// A, D, C, B
17

Does setTimeout() run a callback after the exact delay?

No. The delay is the minimum time before the callback becomes eligible to run. It must still wait for the call stack and earlier queued work. Even a delay of zero runs later, after the current synchronous code and pending microtasks.

18

What is a Promise?

A Promise represents the eventual result of an asynchronous operation. It starts pending and becomes fulfilled with a value or rejected with a reason. then(), catch(), and finally() return new promises, which allows operations and error handling to be chained.

fetch("/api/profile")
  .then((response) => response.json())
  .then((profile) => console.log(profile))
  .catch((error) => console.error(error));
19

How do async and await work?

An async function always returns a Promise. await pauses only that async function until the supplied value settles, so it does not block the main thread. A fulfilled Promise produces its value, while a rejected Promise throws at the await expression and can be handled with try and catch.

async function loadProfile() {
  try {
    const response = await fetch("/api/profile");
    return await response.json();
  } catch (error) {
    console.error(error);
    throw error;
  }
}
20

What is the difference between Promise.all() and Promise.race()?

Promise.all() fulfills when every input fulfills and preserves the input order of results. It rejects as soon as any input rejects. Promise.race() settles as soon as the first input settles, whether that result is a fulfillment or rejection.

const [user, posts] = await Promise.all([
  fetchUser(),
  fetchPosts(),
]);

const firstResult = await Promise.race([requestA(), requestB()]);
21

What is a closure?

A closure is a function together with access to the lexical environment where it was created. It can keep using outer variables even after the outer function has returned. Closures are useful for private state, callbacks, and function factories.

function createCounter() {
  let count = 0;
  return () => ++count;
}

const next = createCounter();
next(); // 1
next(); // 2
22

How is the value of this determined in JavaScript?

For a regular function, this usually depends on how the function is called. A method call uses the object before the dot, call(), apply(), or bind() can set it explicitly, and new creates a new instance. A plain strict-mode function call uses undefined. Arrow functions capture this from their surrounding scope.

const user = {
  name: "Ira",
  showName() {
    return this.name;
  },
};

user.showName(); // Ira
23

What is the difference between call(), apply(), and bind()?

call() invokes a function immediately with an explicit this value and separate arguments. apply() also invokes it immediately but accepts the arguments as an array-like value. bind() returns a new function with this and optional leading arguments fixed for later calls.

function greet(greeting, punctuation) {
  return `${greeting}, ${this.name}${punctuation}`;
}

const person = { name: "Dev" };
greet.call(person, "Hello", "!");
greet.apply(person, ["Hello", "!"]);
const greetDev = greet.bind(person, "Hello");
24

What is an IIFE, and when is it useful?

An Immediately Invoked Function Expression is a function expression that runs as soon as it is created. It was commonly used to create private scope before let, const, and ES modules. It can still be useful for one-time initialization or an isolated async block, but modules and block scope often replace it.

(() => {
  const privateValue = 42;
  console.log(privateValue);
})();
25

What is an event in JavaScript?

An event is a notification that something happened, such as a click, key press, form submission, network state change, or completed resource load. Code can respond by registering an event listener on an EventTarget.

const button = document.querySelector("button");
button.addEventListener("click", (event) => {
  console.log(event.target);
});
26

What are event capturing and event bubbling?

A DOM event first travels from the document toward the target during the capture phase. It reaches the target, then usually travels back through ancestors during the bubble phase. Listeners use the bubble phase by default and can opt into capture with the capture option.

parent.addEventListener("click", handleCapture, { capture: true });
parent.addEventListener("click", handleBubble);
27

What is event delegation?

Event delegation attaches one listener to a shared ancestor and uses event bubbling to handle events from its descendants. It reduces the number of listeners and also works for matching children added later. Use closest() and confirm the matched element belongs to the intended container.

list.addEventListener("click", (event) => {
  const button = event.target.closest("button[data-id]");
  if (!button || !list.contains(button)) return;
  console.log(button.dataset.id);
});
28

How do localStorage and sessionStorage differ?

Both store string key-value pairs for one origin and provide synchronous APIs. localStorage persists across browser sessions until it is cleared. sessionStorage is limited to a page session, usually one tab, and is cleared when that session ends. Neither should store secrets, and large or frequent writes can block the main thread.

localStorage.setItem("theme", "dark");
const theme = localStorage.getItem("theme");

sessionStorage.setItem("draft", "In progress");
29

What do JSON.parse() and JSON.stringify() do?

JSON.parse() converts valid JSON text into a JavaScript value and throws a SyntaxError for invalid JSON. JSON.stringify() converts a supported JavaScript value into JSON text. Functions, undefined, and symbols are omitted from objects, and circular references cause an error unless handled separately.

const text = JSON.stringify({ name: "Ari", active: true });
const value = JSON.parse(text);
30

What are JavaScript modules?

Modules split code into files with explicit imports and exports. ES modules have their own top-level scope, run in strict mode, and are evaluated once per module instance. Static imports also let tools analyze dependencies before execution.

// math.js
export const add = (a, b) => a + b;

// app.js
import { add } from "./math.js";
31

How does prototypal inheritance work?

Every ordinary object can have another object as its prototype. When a property is not found directly on an object, JavaScript follows the prototype chain until it finds the property or reaches null. Class syntax is built on this prototype system.

const animal = { speak: () => "sound" };
const dog = Object.create(animal);

dog.speak(); // Found through the prototype chain
32

What does Object.create() do?

Object.create() creates a new object with the supplied object as its prototype. It can also define own properties through property descriptors. Passing null creates an object with no Object.prototype in its chain.

const dictionary = Object.create(null);
dictionary.answer = 42;

const base = { enabled: true };
const item = Object.create(base);
33

What is the difference between a shallow copy and a deep copy?

A shallow copy creates a new outer object but keeps references to nested objects. A deep copy also creates independent copies of nested supported values. Spread syntax and Object.assign() are shallow. structuredClone() can deeply clone many built-in data types, but it cannot clone every JavaScript value, such as functions.

const original = { profile: { name: "Sam" } };
const shallow = { ...original };
shallow.profile.name = "Lee"; // Also changes original.profile

const deep = structuredClone(original);
34

How do try, catch, finally, and throw work?

Code in try is monitored for exceptions. catch handles an exception thrown while that code runs. finally runs after try and catch whether an exception occurred or not, which makes it useful for cleanup. throw creates an exception, and any JavaScript value can be thrown, although Error objects provide better debugging information.

try {
  if (!response.ok) {
    throw new Error("Request failed");
  }
} catch (error) {
  console.error(error);
} finally {
  hideLoadingState();
}
35

How does JavaScript manage memory?

JavaScript engines allocate memory for values and automatically reclaim objects that are no longer reachable. Developers do not free memory manually, but reachable objects can still cause leaks. Common causes include forgotten event listeners, timers, caches, and closures that retain large object graphs.

36

What is the difference between Map and WeakMap?

Map accepts keys of any type, is iterable, exposes its size, and keeps strong references to its keys. WeakMap accepts objects or non-registered symbols as keys, is not iterable, and does not prevent an object key from being garbage collected. WeakMap is useful for metadata tied to an object's lifetime.

37

What is a Proxy object?

A Proxy wraps an object or function and intercepts operations through traps. Traps can customize property reads, writes, deletion, function calls, construction, and other internal operations. A Proxy should preserve JavaScript's required object invariants.

const settings = new Proxy({}, {
  get(target, property) {
    return property in target ? target[property] : "default";
  },
});
38

What are generator functions and yield?

A generator function returns an iterator and can pause at each yield expression. Calling next() resumes execution until the next yield or return. Generators are useful for lazy sequences, custom iteration, and controlled workflows.

function* ids() {
  yield 1;
  yield 2;
}

const iterator = ids();
iterator.next(); // { value: 1, done: false }
39

What is a Symbol?

A Symbol is a unique primitive value. Symbols are often used as property keys that avoid name collisions. Well-known symbols, such as Symbol.iterator, let objects participate in built-in JavaScript protocols.

const id = Symbol("id");
const user = { [id]: 123 };

user[id]; // 123
40

What is function currying?

Currying transforms a function that accepts several arguments into a sequence of functions that each accept one argument. It can help create specialized reusable functions, although it should be used only when it makes the code clearer.

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

What is the difference between debouncing and throttling?

Debouncing waits until calls stop for a chosen period before running, which is useful for search input or validation. Throttling limits execution to at most once during each interval, which is useful for frequent events such as pointer movement or resizing. Both reduce unnecessary work, but they preserve different timing behavior.