Introduction

JavaScript interviews rarely test only syntax — they test whether you understand why the language behaves the way it does. This guide covers the most frequently asked JavaScript interview questions and answers, from basic concepts to tricky output-based questions, each explained with a simple example anyone can follow.

Section 1: JavaScript Basics

Q1. What are the different data types in JavaScript?

Answer: JavaScript has two categories of data types:

  • Primitive: String, Number, Boolean, Undefined, Null, Symbol, BigInt
  • Non-primitive (Reference): Object, Array, Function

Example:

let age = 28;              // Number
let name = "Priya";        // String
let isActive = true;       // Boolean
let user = { name, age };  // Object (reference type)

Primitive values are copied by value; objects are copied by reference — this difference causes a lot of confusion for beginners.

Q2. What is hoisting in JavaScript?

Answer: Hoisting means variable and function declarations are moved to the top of their scope during the compile phase, before the code actually runs. Only the declaration is hoisted, not the value.

Example:

console.log(a); // undefined, not an error
var a = 10;

console.log(b); // ReferenceError
let b = 20;

var is hoisted and initialized as undefined. let and const are hoisted too, but stay in a “temporal dead zone” until their line actually executes.

Q3. Difference between null and undefined?

Answer: undefined means a variable has been declared but not yet assigned a value. null is an intentional assignment representing “no value.”

let a;
console.log(a);      // undefined (JS assigned this automatically)

let b = null;
console.log(b);      // null (developer assigned this on purpose)

console.log(typeof a); // "undefined"
console.log(typeof b); // "object" (a known long-standing quirk in JS)

Section 2: Functions, Scope & Closures

Q4. What is a closure? Explain with a real example.

Answer: A closure is formed when an inner function keeps access to variables from its outer function, even after the outer function has finished executing.

function createBankAccount(balance) {
  return {
    deposit(amount) { balance += amount; return balance; },
    withdraw(amount) { balance -= amount; return balance; }
  };
}

const account = createBankAccount(1000);
console.log(account.deposit(500));  // 1500
console.log(account.withdraw(200)); // 1300

Here, balance stays private — it can only be changed through the returned methods. This is the basis of data encapsulation in JavaScript.

Q5. What is the difference between function declaration and function expression?

Answer: A function declaration is hoisted completely (can be called before it appears in code). A function expression is only hoisted as a variable, not as a callable function.

greet(); // works fine
function greet() { console.log("Hello"); }

sayHi(); // TypeError: sayHi is not a function
var sayHi = function () { console.log("Hi"); };

Q6. What is the “this” keyword, and how does arrow function behavior differ?

Answer: In a regular function, this depends on how the function is called. An arrow function does not have its own this — it uses the this value from where it was defined (lexical scoping).

const user = {
  name: "Rahul",
  regular: function () { console.log(this.name); },
  arrow: () => { console.log(this.name); }
};

user.regular(); // "Rahul"
user.arrow();   // undefined (this refers to outer/global scope, not user)

Q7. Explain call(), apply(), and bind().

Answer: All three let you control what this refers to inside a function.

  • call() — invokes immediately, arguments passed one by one.
  • apply() — invokes immediately, arguments passed as an array.
  • bind() — returns a new function with this fixed, to be called later.
function introduce(city, country) {
  console.log(`${this.name} lives in ${city}, ${country}`);
}

const person = { name: "Anita" };

introduce.call(person, "Pune", "India");
introduce.apply(person, ["Pune", "India"]);

const boundFn = introduce.bind(person, "Pune", "India");
boundFn(); // called later

Section 3: Asynchronous JavaScript

Q8. What is the event loop?

Answer: JavaScript runs on a single thread. The event loop manages how asynchronous code (timers, promises, API calls) gets executed without blocking the main thread — by using a call stack, a microtask queue, and a macrotask queue.

console.log("Start");

setTimeout(() => console.log("Timeout"), 0);

Promise.resolve().then(() => console.log("Promise"));

console.log("End");

// Output order: Start, End, Promise, Timeout

Microtasks (Promises) always run before macrotasks (setTimeout), even if the timer delay is 0.

Q9. What is a Promise, and what are its three states?

Answer: A Promise represents a value that will be available now, later, or never. Its three states are pending, fulfilled, and rejected.

const fetchData = new Promise((resolve, reject) => {
  const success = true;
  setTimeout(() => {
    success ? resolve("Data loaded") : reject("Error loading data");
  }, 1000);
});

fetchData
  .then(result => console.log(result))
  .catch(error => console.log(error));

Q10. How does async/await simplify Promises?

Answer: async/await is syntax built on top of Promises that lets asynchronous code read like normal synchronous code, making it easier to follow and debug.

async function getUser() {
  try {
    const response = await fetch("/api/user/1");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.log("Something went wrong:", error);
  }
}

Q11. Difference between Promise.all(), Promise.race(), and Promise.allSettled()?

Answer:

  • Promise.all() — waits for all promises; fails fast if any one rejects.
  • Promise.race() — resolves/rejects as soon as the first promise settles.
  • Promise.allSettled() — waits for all, and returns status of each (never fails fast).
const p1 = Promise.resolve("A");
const p2 = Promise.reject("B failed");
const p3 = Promise.resolve("C");

Promise.allSettled([p1, p2, p3]).then(console.log);
// [{status:"fulfilled",value:"A"}, {status:"rejected",reason:"B failed"}, {status:"fulfilled",value:"C"}]

Section 4: ES6+ Features

Q12. Difference between var, let, and const?

Answer:

Feature var let const
Scope Function Block Block
Redeclare Yes No No
Reassign Yes Yes No
Hoisting Initialized as undefined Temporal dead zone Temporal dead zone

Q13. What is destructuring? Give an example.

Answer: Destructuring lets you unpack values from arrays or properties from objects into separate variables in one line.

const user = { name: "Dev", age: 30, city: "Mumbai" };
const { name, city } = user;
console.log(name, city); // "Dev" "Mumbai"

const colors = ["red", "green", "blue"];
const [first, , third] = colors;
console.log(first, third); // "red" "blue"

Q14. What is the spread operator vs rest operator?

Answer: Both use ... but opposite purposes — spread expands values, rest collects them.

// Spread — expands an array into individual elements
const nums = [1, 2, 3];
const copy = [...nums, 4, 5];
console.log(copy); // [1,2,3,4,5]

// Rest — collects remaining arguments into an array
function sum(...numbers) {
  return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4)); // 10

Q15. What are template literals?

Answer: Template literals use backticks and allow embedded expressions and multi-line strings without string concatenation.

const name = "Sara";
const age = 24;
console.log(`${name} is ${age} years old.`);

Section 5: Objects, Arrays & Prototypes

Q16. What is prototypal inheritance?

Answer: Every JavaScript object has an internal link to another object called its prototype. When a property isn’t found on the object itself, JavaScript looks up the prototype chain.

const animal = { eats: true };
const rabbit = Object.create(animal);
rabbit.jumps = true;

console.log(rabbit.eats);  // true (inherited from prototype)
console.log(rabbit.jumps); // true (own property)

Q17. Difference between map(), filter(), and reduce()?

Answer:

  • map() — transforms each element, returns a new array of the same length.
  • filter() — returns a new array with only elements that pass a condition.
  • reduce() — combines all elements into a single value.
const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(n => n * 2);         // [2,4,6,8,10]
const evens = numbers.filter(n => n % 2 === 0);  // [2,4]
const total = numbers.reduce((sum, n) => sum + n, 0); // 15

Q18. How do you deep clone an object in JavaScript?

Answer: A shallow copy (like spread or Object.assign) only copies the first level. For nested objects, you need a deep clone.

const original = { name: "Kiran", address: { city: "Delhi" } };

// Shallow copy - nested object is still shared
const shallow = { ...original };
shallow.address.city = "Noida";
console.log(original.address.city); // "Noida" (changed unintentionally!)

// Deep clone - safe
const deep = structuredClone(original);
deep.address.city = "Jaipur";
console.log(original.address.city); // unaffected

Section 6: Output-Based / Tricky Questions

Q19. What will this code print, and why?

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3

for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 100);
}
// Output: 0, 1, 2

Answer: var is function-scoped, so all three callbacks share the same i, which is 3 by the time the timers run. let creates a fresh block-scoped variable for each loop iteration, so each callback captures its own value.

Q20. What’s the output, and why?

console.log([] == false);   // true
console.log([] === false);  // false
console.log(null == undefined);  // true
console.log(null === undefined); // false

Answer: == allows type coercion before comparing — an empty array gets converted to an empty string, then to 0, which loosely equals false. null and undefined are loosely equal to each other but are different types, so === returns false.

Quick Tips Before Your JavaScript Interview

  • Always explain your answer with a short code snippet — interviewers value clarity over memorized definitions.
  • Practice output-based questions separately — they test real understanding, not just theory.
  • Be ready to explain the “why” behind common quirks like hoisting, closures, and type coercion.
  • For experienced roles, expect follow-up questions on performance, memory leaks, and async error handling.