JavaScript Interview Questions and Answers
What is JavaScript and how is it different from Java?
JavaScript is a lightweight, interpreted, and dynamic programming language primarily used to make web pages interactive. Despite its name, JavaScript has almost nothing in common with Java — they are completely different languages designed for different purposes. JavaScript runs directly in the browser (and on servers via Node.js), while Java is a compiled, object-oriented language used for building desktop apps, Android apps, and backend systems.
JavaScript is single-threaded, event-driven, and supports first-class functions. It follows the ECMAScript standard, with modern versions (ES6 and beyond) introducing powerful features like arrow functions, promises, and modules.
🇮🇳 Hindi में समझें
JavaScript एक programming language है जो web pages को interactive बनाती है। Java और JavaScript दोनों अलग-अलग languages हैं — नाम similar होने के बावजूद इनका कोई direct connection नहीं है। JavaScript browser में directly चलती है और pages को dynamic बनाती है।
What is the difference between var, let, and const?
This is one of the most frequently asked JavaScript interview questions. All three are used to declare variables, but they differ in scope, hoisting behavior, and reassignment rules.
- var— Function-scoped, hoisted to the top of its function, can be re-declared and updated. Often causes bugs due to unexpected scoping.
- let— Block-scoped (limited to {curly braces}), not re-declarable in the same scope, can be updated. Preferred for variables that change.
- const— Block-scoped, cannot be reassigned after declaration. Best for values that should not change. Note: objects/arrays declared with const can still have their properties mutated.
var name = "Alice"; // function-scoped
let age = 25; // block-scoped, can change
const PI = 3.14; // block-scoped, cannot reassign
if (true) {
var name = "Bob"; // overwrites outer var!
let age = 30; // new variable, only inside this block
}
console.log(name); // "Bob" — var leaked out!
console.log(age); // 25 — let stayed inside block
🇮🇳 Hindi में समझें
var पुराना तरीका है जो पूरे function में accessible होता है और bugs पैदा कर सकता है। let modern तरीका है जो block के अंदर ही रहता है और change हो सकता है। const वो value है जो कभी reassign नहीं होती — जैसे आपकी date of birth।
What is the difference between == and === in JavaScript?
This is a classic JavaScript gotcha. The double equals == performs type coercion — it converts values to the same type before comparing. The triple equals === is the strict equality operator — it checks both value AND type without any conversion.
In interviews, always prefer === unless you have a specific reason for loose comparison. Using == can lead to surprising bugs like 0 == false being true.
console.log(0 == false); // true (type coercion)
console.log(0 === false); // false (different types)
console.log("5" == 5); // true (string converted to number)
console.log("5" === 5); // false (string !== number)
console.log(null == undefined); // true
console.log(null === undefined); // false
🇮🇳 Hindi में समझें
== value compare करते वक्त type को automatically convert कर देता है (type coercion), जिससे unexpected results आ सकते हैं। === strict है — value और type दोनों same होने चाहिए। Interviews में हमेशा === use करें।
What is a closure in JavaScript?
A closure is one of the most powerful and frequently tested concepts in JavaScript. A closure is created when a function "remembers" the variables from its outer (enclosing) scope even after the outer function has finished executing. In simple terms — an inner function has access to the variables of its outer function even when called outside of it.
Closures are used in callbacks, data privacy, event handlers, factory functions, and module patterns. Every function in JavaScript is technically a closure because it closes over its surrounding scope.
function makeCounter() {
let count = 0; // outer variable
return function() { // inner function (closure)
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// 'count' is remembered across calls!
🇮🇳 Hindi में समझें
Closure का मतलब है — एक inner function को अपने outer function की variables याद रहती हैं, भले ही outer function execute होकर खत्म हो जाए। यह JavaScript का एक बेहद important concept है जो privacy और state management में use होता है।
What is a Promise in JavaScript and how does it work?
A Promise is an object that represents the eventual completion or failure of an asynchronous operation. It is JavaScript's way of handling async tasks like API calls, file reads, or timers without blocking the main thread. A Promise can be in one of three states: Pending (initial state), Fulfilled (operation succeeded), or Rejected (operation failed).
Promises replaced the messy "callback hell" pattern and made async code much more readable. They are the foundation of async/await syntax introduced in ES2017.
const fetchData = new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve("Data fetched successfully!");
} else {
reject("Something went wrong.");
}
});
fetchData
.then(result => console.log(result)) // "Data fetched..."
.catch(error => console.log(error))
.finally(() => console.log("Done!"));
🇮🇳 Hindi में समझें
Promise एक object है जो बताता है कि कोई async काम future में complete होगा या fail होगा। जैसे online order place करने पर आपको एक promise मिलती है delivery का — काम हो गया तो resolve, नहीं हुआ तो reject। यह callback hell से बचने का सबसे modern तरीका है।
What is the difference between arrow functions and regular functions?
Arrow functions (=>) were introduced in ES6 and are a shorter syntax for writing functions. But beyond syntax, they have one critical behavioral difference — they do not have their own this. They inherit this from the surrounding lexical context, while regular functions define their own this based on how they are called.
Arrow functions also cannot be used as constructors, don't have an arguments object, and cannot use yield. They are ideal for callbacks and array methods.
// Regular function
const obj = {
name: "Ravi",
greet: function() {
console.log("Hello, " + this.name); // "Hello, Ravi"
}
};
// Arrow function — 'this' comes from outer scope
const obj2 = {
name: "Ravi",
greet: () => {
console.log("Hello, " + this.name); // undefined!
}
};
obj.greet(); // Hello, Ravi
obj2.greet(); // Hello, undefined
🇮🇳 Hindi में समझें
Arrow functions ES6 में आई एक short syntax है। सबसे बड़ा अंतर यह है कि arrow functions का खुद का this नहीं होता — वो surrounding scope से this लेती हैं। Regular functions का अपना this होता है जो calling context पर depend करता है।
What is hoisting in JavaScript?
Hoisting is JavaScript's default behavior of moving declarations to the top of their scope before code execution. This means you can use a variable or function before you've written its declaration in the code. However, only declarations are hoisted — not initializations.
Function declarations are fully hoisted (both declaration + body). var declarations are hoisted but initialized as undefined. let and const are hoisted but kept in a "Temporal Dead Zone" — you cannot access them before declaration.
// Function hoisting — works fine
sayHello(); // "Hello!" — called before declaration
function sayHello() {
console.log("Hello!");
}
// var hoisting
console.log(x); // undefined (hoisted, not initialized)
var x = 10;
// let — Temporal Dead Zone
console.log(y); // ReferenceError!
let y = 20;
🇮🇳 Hindi में समझें
Hoisting का मतलब है — JavaScript code run होने से पहले सभी declarations को उनके scope के top पर move कर देती है। var undefined होकर hoist होता है, functions पूरी तरह hoist होते हैं, लेकिन let और const Temporal Dead Zone में रहते हैं — उन्हें पहले access नहीं कर सकते।
What is prototypal inheritance in JavaScript?
In JavaScript, every object has an internal property called [[Prototype]] (accessible via __proto__ or Object.getPrototypeOf()). When you try to access a property on an object and it doesn't exist, JavaScript automatically looks up the prototype chain until it finds it or reaches null. This is called prototypal inheritance.
Unlike class-based inheritance in Java or C++, JavaScript uses prototypes under the hood. ES6 class syntax is just syntactic sugar over this prototype system.
const animal = {
breathe() { return "Breathing..."; }
};
const dog = Object.create(animal); // dog inherits from animal
dog.bark = function() { return "Woof!"; };
console.log(dog.bark()); // "Woof!" — own method
console.log(dog.breathe()); // "Breathing..." — inherited!
console.log(dog.hasOwnProperty("breathe")); // false
🇮🇳 Hindi में समझें
JavaScript में हर object का एक prototype होता है जिससे वो properties और methods inherit करता है। अगर कोई property object में नहीं मिली, तो JS उसके prototype में ढूंढती है — यही prototype chain है। ES6 की class syntax इसी concept पर based है।
What is the Event Loop in JavaScript?
JavaScript is single-threaded — it can only do one thing at a time. But it handles asynchronous operations (like API calls, timers, and events) without blocking thanks to the Event Loop. The event loop continuously checks if the Call Stack is empty, and if so, it picks up the next task from the Callback Queue and pushes it onto the stack.
The execution order is: Call Stack → Microtask Queue (Promises) → Macro Task Queue (setTimeout, setInterval). Understanding this is key to avoiding race conditions and understanding why setTimeout(fn, 0) doesn't run immediately.
console.log("1 - Start");
setTimeout(() => console.log("2 - setTimeout"), 0);
Promise.resolve().then(() => console.log("3 - Promise"));
console.log("4 - End");
// Output order:
// 1 - Start
// 4 - End
// 3 - Promise (microtask — runs before setTimeout)
// 2 - setTimeout (macrotask)
🇮🇳 Hindi में समझें
JavaScript single-threaded है लेकिन Event Loop की वजह से async tasks handle कर सकती है। Call Stack खाली होने पर Event Loop Microtask Queue (Promises) और फिर Macrotask Queue (setTimeout) से tasks उठाता है। यही reason है कि setTimeout(fn, 0) भी synchronous code के बाद run होता है।
What is destructuring in JavaScript?
Destructuring is an ES6 feature that allows you to unpack values from arrays or properties from objects into distinct variables. It makes code cleaner, more readable, and reduces repetitive property access. You can destructure objects, arrays, nested structures, and even function parameters.
// Object Destructuring
const user = { name: "Priya", age: 28, city: "Mumbai" };
const { name, age, city } = user;
console.log(name); // "Priya"
// Array Destructuring
const [first, second, ...rest] = [10, 20, 30, 40];
console.log(first); // 10
console.log(rest); // [30, 40]
// Default values
const { country = "India" } = user;
console.log(country); // "India" (default used)
🇮🇳 Hindi में समझें
Destructuring एक ES6 feature है जिससे आप object या array की values को एक ही line में अलग-अलग variables में store कर सकते हैं। यह code को short और readable बनाता है। React में props destructuring बहुत commonly use होता है।
What is the difference between map(), filter(), and reduce()?
These three are the most powerful and commonly used array methods in JavaScript — and frequently tested in interviews. All three are higher-order functions (they take a function as an argument) and do not mutate the original array.
- map() — Transforms every element and returns a new array of the same length.
- filter() — Returns a new array with only elements that pass a condition.
- reduce() — Reduces the array to a single value (sum, product, object, etc.) using an accumulator.
const nums = [1, 2, 3, 4, 5]; const doubled = nums.map(n => n * 2); // [2, 4, 6, 8, 10] const evens = nums.filter(n => n % 2 === 0); // [2, 4] const sum = nums.reduce((acc, n) => acc + n, 0); // 15
🇮🇳 Hindi में समझें
map() हर element को transform करता है, filter() सिर्फ वही elements रखता है जो condition pass करते हैं, और reduce() पूरे array को एक single value में convert करता है। ये तीनों original array को नहीं बदलते।
What is debouncing and throttling in JavaScript?
Both debouncing and throttling are performance optimization techniques used to control how frequently a function is called — especially in events like scrolling, resizing, or typing where events fire rapidly.
Debouncing — delays execution until a certain time has passed since the last event. Useful for search inputs (wait for user to stop typing before calling API). Throttling — ensures a function is called at most once per specified time period. Useful for scroll events (run at most every 200ms).
// Debounce
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
// Usage: search input
const search = debounce((query) => {
console.log("Searching:", query);
}, 500);
// Throttle
function throttle(fn, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
🇮🇳 Hindi में समझें
Debounce: user जब तक type करता रहे, API call मत करो — रुकने के बाद ही call करो। Throttle: चाहे user कितना भी scroll करे, function हर 200ms में सिर्फ एक बार ही चलेगा। दोनों performance बेहतर बनाने के लिए use होते हैं।
What is the difference between null, undefined, and NaN?
These three are distinct "empty" or "invalid" values in JavaScript and often confuse beginners. undefined means a variable has been declared but not assigned a value — JavaScript sets it automatically. null is an intentional absence of value — a developer explicitly sets it. NaN (Not a Number) is the result of an invalid mathematical operation like dividing a string by a number.
let a;
console.log(a); // undefined (declared, not assigned)
let b = null;
console.log(b); // null (intentionally empty)
console.log(typeof null); // "object" (famous JS bug!)
console.log(typeof undefined); // "undefined"
console.log("abc" * 2); // NaN
console.log(isNaN(NaN)); // true
console.log(NaN === NaN);// false (NaN is not equal to itself!)
🇮🇳 Hindi में समझें
undefined = variable declare हुआ लेकिन value नहीं दी। null = developer ने खुद value को खाली set किया। NaN = गलत math operation का result जैसे "abc" / 2। एक interesting fact: typeof null returns "object" — यह JavaScript की एक पुरानी bug है।
What is async/await and how is it better than Promises?
Introduced in ES2017, async/await is syntactic sugar built on top of Promises. It makes asynchronous code look and behave like synchronous code — much easier to read, write, and debug. An async function always returns a Promise. The await keyword pauses execution inside the function until the Promise resolves.
It doesn't replace Promises — it uses them under the hood. The key benefit is readability: no more .then().catch() chains scattered across your code.
// Using Promises (harder to read for complex chains)
fetch("https://api.example.com/user")
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.log(err));
// Using async/await (clean and readable)
async function getUser() {
try {
const res = await fetch("https://api.example.com/user");
const data = await res.json();
console.log(data);
} catch (err) {
console.log("Error:", err);
}
}
getUser();
🇮🇳 Hindi में समझें
async/await Promises के ऊपर बनी एक cleaner syntax है। async function हमेशा Promise return करता है, और await Promise resolve होने तक रुकता है। यह code को पढ़ना और debug करना बहुत आसान बना देता है — particularly जब कई async operations एक के बाद एक हों।
What are classes in JavaScript and how do they work?
ES6 introduced class syntax as a cleaner way to create objects and deal with inheritance. Under the hood, JavaScript classes are still built on the prototype system — they're syntactic sugar that makes OOP patterns easier to write and read. A class can have a constructor, methods, static methods, and can extend other classes using extends and super.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound.`;
}
}
class Dog extends Animal {
speak() {
return `${this.name} barks!`;
}
}
const d = new Dog("Bruno");
console.log(d.speak()); // "Bruno barks!"
console.log(d instanceof Animal); // true
🇮🇳 Hindi में समझें
JavaScript में class ES6 में आई एक syntax है जो OOP (Object-Oriented Programming) को easier बनाती है। यह internally prototype पर ही काम करती है। extends से inheritance मिलती है और super() से parent class का constructor call होता है।
What is the difference between CommonJS and ES Modules?
JavaScript has two main module systems. CommonJS (CJS) was built for Node.js and uses require() and module.exports. It loads modules synchronously. ES Modules (ESM) is the modern standard introduced in ES6 that uses import and export. It loads modules asynchronously and enables static analysis and tree shaking (unused code elimination).
Today, most modern JavaScript (React, Vue, Node.js with "type": "module") uses ES Modules. Knowing both is essential for full-stack and Node.js interviews.
// CommonJS (Node.js)
const fs = require("fs");
module.exports = { myFunction };
// ES Modules (modern JS)
import fs from "fs";
export const myFunction = () => {};
export default myFunction;
// Named vs default exports
export const PI = 3.14; // named
export default function add() {} // default (only one per file)
🇮🇳 Hindi में समझें
CommonJS पुराना Node.js module system है जो require() use करता है। ES Modules modern standard है जो import/export use करता है और browsers + Node.js दोनों में support होता है। आज ज़्यादातर modern projects ES Modules use करते हैं।
What are generators and iterators in JavaScript?
A generator is a special function that can pause its execution and resume later. Defined with function*, it uses yield to return values one at a time. Each call to .next() runs the function until the next yield. They are used for lazy evaluation, infinite sequences, async control flow, and state machines.
An iterator is any object that implements the iterator protocol — it has a next() method that returns { value, done }. All generators are iterators, but not all iterators are generators.
function* countUp() {
yield 1;
yield 2;
yield 3;
}
const gen = countUp();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
// Infinite sequence
function* infiniteId() {
let id = 1;
while (true) yield id++;
}
🇮🇳 Hindi में समझें
Generator function function* से define होती है और yield से pause होती है। हर बार .next() call करने पर वो अगली value देती है। यह large data sets को memory-efficiently process करने और async flows को control करने के लिए use होती है।
What is a WeakMap and WeakSet, and when should you use them?
A WeakMap is a collection of key-value pairs where keys must be objects (not primitives), and keys are held weakly — meaning if there are no other references to the key object, it can be garbage collected automatically. A WeakSet is similar but only holds objects (no duplicates) with weak references. Neither is iterable.
They are primarily used for memory-safe caching and storing private data associated with DOM elements or objects without preventing garbage collection — ideal in frameworks and libraries.
let obj = { name: "Alice" };
const wm = new WeakMap();
wm.set(obj, "some private data");
console.log(wm.get(obj)); // "some private data"
// When obj is set to null, it becomes eligible for GC
obj = null;
// The WeakMap entry is cleaned up automatically — no memory leak!
// Use case: DOM element cache
const cache = new WeakMap();
function processElement(el) {
if (!cache.has(el)) cache.set(el, computeResult(el));
return cache.get(el);
}
🇮🇳 Hindi में समझें
WeakMap और WeakSet में objects को weakly store किया जाता है — जैसे ही उस object का कोई और reference नहीं रहता, memory automatically free हो जाती है। यह memory leaks से बचाने के लिए DOM element caching और private data storage में use होते हैं।
What is currying in JavaScript?
Currying is a functional programming technique where a function that takes multiple arguments is transformed into a sequence of functions each taking a single argument. Named after mathematician Haskell Curry, it enables partial application — pre-filling some arguments and getting a new function that takes the rest.
Currying improves code reusability, makes functions more composable, and is heavily used in functional programming libraries like Lodash and Ramda, and in React with HOCs (Higher-Order Components).
// Regular function
function add(a, b, c) { return a + b + c; }
// Curried version
function curriedAdd(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
console.log(curriedAdd(1)(2)(3)); // 6
// Partial application
const addFive = curriedAdd(5);
const addFiveAndThree = addFive(3);
console.log(addFiveAndThree(2)); // 10
🇮🇳 Hindi में समझें
Currying एक technique है जहाँ एक function जो multiple arguments लेता है, उसे chain of functions में convert किया जाता है जो एक-एक argument लेते हैं। इससे code reusable बनता है और आप function को partially apply करके नए functions बना सकते हैं।
What is the Proxy object in JavaScript and what are its use cases?
The Proxy object (introduced in ES6) allows you to create a wrapper around another object and intercept fundamental operations — like reading properties, writing values, function calls, and more. You define a handler with traps (special methods like get, set, deleteProperty) that run when the target object is interacted with.
Proxies are at the heart of Vue 3's reactivity system. They're used for validation, logging, default values, API mocking, and building reactive data systems.
const validator = {
set(target, prop, value) {
if (prop === "age" && typeof value !== "number") {
throw new TypeError("Age must be a number");
}
if (prop === "age" && value < 0) {
throw new RangeError("Age must be positive");
}
target[prop] = value;
return true;
}
};
const person = new Proxy({}, validator);
person.name = "Arjun"; // works fine
person.age = 25; // works fine
person.age = "old"; // TypeError: Age must be a number
person.age = -1; // RangeError: Age must be positive
🇮🇳 Hindi में समझें
Proxy object एक wrapper है जो किसी दूसरे object के operations को intercept करता है। जैसे property read/write होने पर आप अपना custom logic चला सकते हैं — validation, logging, या default values। Vue 3 का reactivity system इसी पर based है।