JavaScript Interview Questions and Answer
JavaScript interviews test your understanding of core language features, asynchronous programming, scope, closures, and problem-solving skills. This comprehensive guide covers 50+ frequently asked interview questions — from beginner fundamentals to advanced concepts — with clear explanations and practical code examples.
Quick Jump
1–10: Basics & Variables
var, let, and const?var— function-scoped, can be re-declared, hoisted withundefined.let— block-scoped, cannot be re-declared in the same scope, hoisted but not initialized (Temporal Dead Zone).const— block-scoped, cannot be re-declared or reassigned (but object properties can be mutated).
var a = 1;
let b = 2;
const c = 3;
// c = 4; // TypeError
== and ===?==— abstract equality, performs type coercion before comparison.===— strict equality, compares both value and type without coercion.
console.log(2 == '2'); // true (coercion)
console.log(2 === '2'); // false (type mismatch)
Always prefer === for predictable behavior.
var declarations are hoisted with undefined, while let and const are hoisted but not initialized.
console.log(x); // undefined (hoisted)
var x = 5;
// console.log(y); // ReferenceError (TDZ)
let y = 10;
Function declarations are hoisted entirely.
null and undefined?undefined— a variable has been declared but not assigned a value.null— an intentionally assigned empty value, representing "no value".
let a;
console.log(a); // undefined
let b = null;
console.log(b); // null
console.log(typeof null); // 'object' (historical quirk)
string, number, bigint, boolean, undefined, symbol, null.Non-primitive:
object (includes arrays, functions, dates, etc.).
console.log(typeof 'hello'); // string
console.log(typeof 42); // number
console.log(typeof {}); // object
console.log(typeof []); // object (array is an object)
let or const variable. Accessing the variable during this period throws a ReferenceError.
// TDZ starts here
console.log(x); // ReferenceError
let x = 5; // TDZ ends here
NaN and undefined?NaN (Not-a-Number) is a numeric value that represents an invalid number. undefined means a variable has no value assigned. typeof NaN returns 'number'.
console.log(typeof NaN); // 'number'
console.log(NaN === NaN); // false (NaN is not equal to itself)
typeof operator used for?typeof returns a string indicating the type of the operand. It's useful for type checking, but has quirks (e.g., typeof null === 'object').
console.log(typeof 42); // 'number'
console.log(typeof 'hello'); // 'string'
console.log(typeof undefined); // 'undefined'
console.log(typeof null); // 'object' (bug in JS)
console.log(typeof function(){}); // 'function'
++i and i++?++i(pre-increment) — increments the value and returns the new value.i++(post-increment) — returns the current value, then increments.
let i = 5;
console.log(++i); // 6 (i becomes 6, then logged)
let j = 5;
console.log(j++); // 5 (logged, then j becomes 6)
Array.isArray() — it's the safest method.
let arr = [1, 2, 3];
console.log(Array.isArray(arr)); // true
console.log(Array.isArray({})); // false
instanceof Array can fail across frames.
11–20: Functions & Scope
function outer() {
let count = 0;
return function inner() {
count++;
return count;
};
}
const counter = outer();
console.log(counter()); // 1
console.log(counter()); // 2
Closures are used for data privacy, currying, and event handlers.
- Function declaration: hoisted entirely, can be called before definition.
- Function expression: not hoisted, only defined when the interpreter reaches that line.
// Function declaration
console.log(add(2, 3)); // 5
function add(a, b) { return a + b; }
// Function expression
// console.log(sub(5, 2)); // TypeError
const sub = function(a, b) { return a - b; };
- No
thisbinding — they inheritthisfrom the enclosing scope. - Cannot be used as constructors (no
new). - No
argumentsobject (use rest parameters instead). - Implicit return for single-expression bodies.
const add = (a, b) => a + b;
const square = n => n * n;
// this binding example
const obj = {
name: 'John',
greet: () => console.log(this.name) // this refers to outer scope
};
this keyword in JavaScript?this refers to the context in which a function is called. Its value depends on how the function is invoked:
- Global:
this===window(browser) orglobal(Node). - Object method:
thisrefers to the object. - Constructor:
thisrefers to the new instance. - Arrow function:
thisis lexically bound. - Explicit binding:
call(),apply(),bind().
const obj = {
name: 'Alice',
greet() { console.log(this.name); }
};
obj.greet(); // 'Alice'
call(), apply(), and bind()?this value explicitly.
call()— invokes the function with a giventhisand arguments (comma-separated).apply()— same ascall(), but arguments are passed as an array.bind()— returns a new function with a boundthis(does not invoke immediately).
function greet(greeting) {
console.log(greeting + ', ' + this.name);
}
const user = { name: 'Bob' };
greet.call(user, 'Hello'); // 'Hello, Bob'
greet.apply(user, ['Hi']); // 'Hi, Bob'
const bound = greet.bind(user, 'Hey');
bound(); // 'Hey, Bob'
arguments and rest parameters?arguments— an array-like object available inside regular functions (not arrow functions). Haslengthbut lacks array methods.- Rest parameters —
...argssyntax, returns a real array, works in arrow functions.
function regular() {
console.log(arguments.length);
}
const arrow = (...args) => console.log(args.length);
regular(1, 2, 3); // 3
arrow(1, 2, 3); // 3
map(), filter(), reduce().
function applyTwice(fn, value) {
return fn(fn(value));
}
const result = applyTwice(x => x * 2, 3);
console.log(result); // 12
- Always returns the same output for the same input (deterministic).
- No side effects (does not modify external state).
// Pure
function add(a, b) { return a + b; }
// Impure (modifies external state)
let count = 0;
function increment() { count++; }
function and => for methods in objects?this binding to the object. Arrow functions inherit this from the outer scope (which may be the global scope), so they are not suitable for object methods if you need to access the object.
const obj = {
name: 'Test',
regular: function() { console.log(this.name); }, // 'Test'
arrow: () => console.log(this.name) // undefined (global this)
};
obj.regular();
obj.arrow();
function fetchData(callback) {
setTimeout(() => {
callback('Data received');
}, 1000);
}
fetchData((data) => console.log(data));
Callbacks are the foundation of asynchronous programming in JavaScript.
21–30: Objects & Prototypes
[[Prototype]] link (accessed via __proto__ or Object.getPrototypeOf()).
const parent = { greet: 'Hello' };
const child = Object.create(parent);
console.log(child.greet); // 'Hello' (inherited)
Object.create() and new?Object.create(proto)— creates a new object with the specified prototype.new Constructor()— creates a new object using a constructor function and sets the prototype toConstructor.prototype.
const proto = { shared: 'value' };
const obj1 = Object.create(proto);
function Person(name) { this.name = name; }
const obj2 = new Person('John');
class syntax in JavaScript?class syntax — syntactic sugar over prototype-based inheritance. It provides a cleaner way to define constructor functions and methods.
class Animal {
constructor(name) { this.name = name; }
speak() { console.log(this.name + ' makes a noise.'); }
}
class Dog extends Animal {
speak() { console.log(this.name + ' barks.'); }
}
const d = new Dog('Rex');
d.speak(); // 'Rex barks.'
Object.freeze() and Object.seal()?Object.freeze()— prevents adding, deleting, or modifying properties (makes the object immutable).Object.seal()— prevents adding or deleting properties, but existing properties can still be modified.
const obj = { a: 1 };
Object.freeze(obj);
obj.a = 2; // silently fails (or TypeError in strict mode)
console.log(obj.a); // 1
- Shallow copy: spread (
{...obj}) orObject.assign({}, obj). - Deep copy:
structuredClone(obj)(modern) orJSON.parse(JSON.stringify(obj))(with limitations).
const original = { a: 1, b: { c: 2 } };
const shallow = { ...original };
shallow.b.c = 42; // modifies original.b.c too
const deep = structuredClone(original);
deep.b.c = 99; // original.b.c remains 42
for...in and for...of?for...in— iterates over enumerable property keys (including inherited) of an object.for...of— iterates over iterable values (arrays, strings, maps, sets, etc.).
const arr = ['a', 'b', 'c'];
for (let key in arr) console.log(key); // '0', '1', '2'
for (let value of arr) console.log(value); // 'a', 'b', 'c'
Map and Object?Map— keys can be any type (including objects), preserves insertion order, hassizeproperty, and is iterable.Object— keys are strings or symbols, has prototype inheritance, and is not directly iterable.
const map = new Map();
map.set('key', 'value');
map.set(42, 'number');
console.log(map.get(42)); // 'number'
Set in JavaScript?Set is a collection of unique values (no duplicates). It maintains insertion order and works with any value type.
const set = new Set([1, 2, 2, 3]);
console.log(set); // Set { 1, 2, 3 }
set.add(4);
console.log(set.has(3)); // true
set.delete(2);
hasOwnProperty() (own property) or the in operator (includes inherited properties).
const obj = { a: 1 };
console.log(obj.hasOwnProperty('a')); // true
console.log(obj.hasOwnProperty('toString')); // false
console.log('toString' in obj); // true (inherited)
Object.keys(), Object.values(), and Object.entries()?Object.keys(obj)— returns an array of enumerable property keys.Object.values(obj)— returns an array of enumerable property values.Object.entries(obj)— returns an array of[key, value]pairs.
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.keys(obj)); // ['a', 'b', 'c']
console.log(Object.values(obj)); // [1, 2, 3]
console.log(Object.entries(obj)); // [['a',1], ['b',2], ['c',3]]
31–40: Async & Event Loop
console.log('Start');
setTimeout(() => console.log('Timeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
console.log('End');
// Output: Start, End, Promise, Timeout
Microtasks (promises) have higher priority than macrotasks (setTimeout).
- Synchronous: code runs sequentially, blocking further execution until the current operation completes.
- Asynchronous: code runs non-blocking; operations like network requests, timers, or file I/O are delegated to the browser/Node, and callbacks are executed later.
// Synchronous
console.log('A');
console.log('B'); // B runs after A
// Asynchronous
console.log('A');
setTimeout(() => console.log('B'), 0);
console.log('C'); // C runs before B
pending, fulfilled, or rejected.
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('Success!'), 1000);
});
promise.then(value => console.log(value)) // 'Success!'
.catch(err => console.error(err));
Promise.all() and Promise.allSettled()?Promise.all()— rejects immediately if any promise rejects; resolves with an array of values if all resolve.Promise.allSettled()— waits for all promises to settle (fulfill or reject) and returns an array of status objects.
Promise.all([p1, p2]).then(v => console.log(v));
Promise.allSettled([p1, p2]).then(results => {
results.forEach(r => console.log(r.status));
});
async/await?async/await is syntactic sugar built on promises, making asynchronous code look and behave more like synchronous code. An async function returns a promise, and await pauses execution until the promise resolves.
async function fetchData() {
try {
const response = await fetch('https://api.example.com');
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
- Promises with chaining (
.then()) async/await(most readable)- Breaking functions into smaller, named functions
// Callback hell
doA(() => {
doB(() => {
doC(() => console.log('Done'));
});
});
// With async/await
async function run() {
await doA();
await doB();
await doC();
console.log('Done');
}
setTimeout and setInterval?setTimeout(fn, delay)— executes the function once after the specified delay.setInterval(fn, interval)— repeatedly executes the function everyintervalmilliseconds.
setTimeout(() => console.log('Once'), 1000);
const id = setInterval(() => console.log('Repeating'), 2000);
clearInterval(id); // stop after some time
- Microtasks:
Promise.then(),queueMicrotask(),MutationObserver. They run immediately after the current stack, before any macrotasks. - Macrotasks:
setTimeout(),setInterval(),I/O,UI rendering. They run in the next iteration of the event loop.
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// Output: 1, 4, 3, 2
fetch() and how does it work?fetch() is a modern API for making network requests. It returns a Promise that resolves to a Response object.
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error(err));
// With async/await
async function getData() {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return data;
}
try/catch with async/await and .catch() with promises?try/catch with await provides a more synchronous-looking syntax for error handling.
// Promise style
fetch(url)
.then(res => res.json())
.catch(err => console.error(err));
// async/await style
try {
const res = await fetch(url);
const data = await res.json();
} catch (err) {
console.error(err);
}
The try/catch approach is often more readable, especially with multiple awaits.
41–50: ES6+ & Advanced
// Array destructuring
const [a, b] = [1, 2];
console.log(a, b); // 1, 2
// Object destructuring
const { name, age } = { name: 'John', age: 30 };
console.log(name, age); // 'John', 30
It can be used with default values, rest patterns, and nested structures.
`) that support embedded expressions via ${} and multi-line strings.
const name = 'Alice';
const greeting = `Hello, ${name}!
Welcome to the course.`;
console.log(greeting);
...) and what is it used for?- Copying arrays/objects:
[...arr],{...obj} - Merging arrays/objects:
[...a, ...b] - Function arguments:
Math.max(...nums)
const arr1 = [1, 2];
const arr2 = [3, 4];
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4]
...args) collects remaining arguments into a real array. It is used in function definitions to handle variable numbers of arguments.
function sum(...numbers) {
return numbers.reduce((acc, n) => acc + n, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
import and require()?require()— CommonJS, synchronous, used in Node.js (and older bundlers).import— ES6 module syntax, asynchronous, used in modern JavaScript (both browser and Node with type:module).
// CommonJS
const module = require('./module');
// ES6
import module from './module.js';
Symbol data type?Symbol is a primitive data type introduced in ES6. Each symbol is unique and immutable, often used as object property keys to avoid name collisions.
const sym1 = Symbol('id');
const sym2 = Symbol('id');
console.log(sym1 === sym2); // false
const obj = { [sym1]: 'value' };
console.log(obj[sym1]); // 'value'
get and set keywords.
const obj = {
_name: '',
get name() { return this._name; },
set name(value) { this._name = value.trim(); }
};
obj.name = ' John ';
console.log(obj.name); // 'John'
forEach() and map()?forEach()— iterates over each element, executes a callback, but does not return a new array (returnsundefined).map()— creates a new array by applying a function to each element.
const arr = [1, 2, 3];
arr.forEach(n => console.log(n)); // logs 1,2,3
const doubled = arr.map(n => n * 2); // [2, 4, 6]
function*) returns a generator object that can be used to control the execution of the function. It uses yield to pause and resume execution.
function* countUp() {
let i = 0;
while (true) {
yield i++;
}
}
const gen = countUp();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
new operator doing under the hood?new Constructor():
- A new empty object is created.
- The object's prototype is set to
Constructor.prototype. - The constructor is called with
thisbound to the new object. - If the constructor returns an object, that object is returned; otherwise, the new object is returned.
function Person(name) { this.name = name; }
const p = new Person('Alice');
console.log(p.name); // 'Alice'
51–55: DOM & Miscellaneous
// Selecting and modifying an element
const el = document.getElementById('myId');
el.textContent = 'New text';
el.style.color = 'red';
document.querySelector('#parent').addEventListener('click', (e) => {
if (e.target.matches('.child')) {
console.log('Child clicked:', e.target.textContent);
}
});
stopPropagation() and preventDefault()?stopPropagation()— prevents the event from bubbling up the DOM tree.preventDefault()— prevents the default action of the event (e.g., form submission, link navigation).
anchor.addEventListener('click', (e) => {
e.preventDefault(); // prevents navigation
e.stopPropagation(); // prevents parent handlers
});
localStorage and sessionStorage?localStorage— persists data even after the browser is closed (no expiration).sessionStorage— persists data only for the duration of the page session (cleared when the tab is closed).
localStorage.setItem('key', 'value');
const val = localStorage.getItem('key');
localStorage.removeItem('key');
Cookie and how does it differ from localStorage?localStorage is client-side only, persists longer, and has a larger limit (~5–10MB).
// Setting a cookie
document.cookie = 'username=John; expires=...; path=/';
// localStorage is simpler
localStorage.setItem('username', 'John');
Pro Tip: Practice writing code for each concept. Understanding the theory is important, but being able to implement solutions quickly is what sets you apart in interviews.
Ready to master real-world JavaScript development?
Learn JavaScript hands-on with mentor-led, live sessions.