Top 100 Advanced JavaScript Interview Questions & Answers [2026]
In today’s highly competitive developer landscape, technical interviews demand far more than surface-level understanding. Particularly when it comes to JavaScript—a language that powers the core of the web and extends across mobile, desktop, and server-side platforms—candidates are expected to demonstrate a command over not just syntax, but deep architectural thinking, performance optimization, and language internals.
To help you excel at the highest levels, DigitalDefynd proudly presents this exhaustive guide: 100 Advanced JavaScript Intelligence Interview Questions & Answers. Designed specifically for experienced developers, senior engineers, technical leads, and architect-level candidates, this resource serves as both an interview preparation tool and a professional reference manual.
This guide is more than a list of questions—it’s a deep dive into the JavaScript engine itself. Each section is crafted to reinforce concepts that reflect real-world challenges and interview expectations at top tech companies and startups alike. Topics span from fundamental yet often misunderstood behaviors like hoisting, closures, and event bubbling—to more complex constructs like concurrency, generator functions, proxies, memory management, and module systems in both browser and Node.js environments.
You’ll explore:
- How the event loop orchestrates asynchronous behavior
- The implications of lexical scoping and closures in scalable applications
- Real uses of
WeakMap,Symbol, and advanced object handling - The mechanics of throttling vs. debouncing for performance tuning
- Practical applications of newer features like
optional chaining,nullish coalescing, andBigInt - Functional programming principles including immutability and pure functions
- Security, optimization, and debugging strategies for production-grade code
Every answer is structured to provide clarity, completeness, and actionable insight. Where necessary, concise code snippets are included—written in compiler-friendly format—so you can immediately test and explore each concept.
At DigitalDefynd, we believe in empowering developers not just to clear interviews, but to master the technologies they work with. Whether you’re preparing for a senior engineering interview, mentoring junior developers, designing large-scale systems, or simply striving to refine your JavaScript expertise, this guide will serve as a reliable and enriching resource.
So if you’re serious about advancing your career and your command of JavaScript, you’re in the right place.
Top 100 Advanced JavaScript Interview Questions & Answers [2026]
1. What is the JavaScript Event Loop, and how does it work internally?
Answer:
The JavaScript Event Loop is the core mechanism that enables asynchronous, non-blocking behavior in JavaScript despite it being single-threaded. It coordinates the execution of code, events, and messages in a way that doesn’t freeze the main thread.
Key Components:
- Call Stack: A LIFO stack where function calls are pushed and popped.
- Web APIs: Provided by the browser or Node.js environment (e.g., DOM timers, HTTP requests).
- Callback Queues:
- Macrotask Queue:
setTimeout,setInterval,I/O, etc. - Microtask Queue:
Promise.then,MutationObserver,queueMicrotask.
- Macrotask Queue:
Execution Flow:
- JavaScript starts executing code synchronously.
- When it encounters asynchronous tasks (e.g.,
setTimeout), it registers them with Web APIs. - Once the Call Stack is empty, the Event Loop prioritizes and pushes microtasks to the stack first, followed by macrotasks.
Example:
console.log('Start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise resolved');
});
console.log('End');
Output:
Start
End
Promise resolved
setTimeout
This shows how microtasks (like promises) execute before macrotasks (like timeouts).
2. Explain closures in JavaScript with practical usage examples.
Answer:
A closure is a function that retains access to its lexical scope, even when executed outside of that scope. Closures are a foundational concept in JavaScript and enable powerful patterns like function factories, private variables, and encapsulation.
How It Works:
When a function is defined inside another function, the inner function forms a closure by capturing variables from the outer function’s scope.
Example: Counter with Closure
function createCounter() {
let count = 0;
return function () {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
Here, the inner function retains access to count even after createCounter has executed. This is closure in action.
Real-World Use Case:
Closures are often used for data hiding:
function secureBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit(amount) {
balance += amount;
return balance;
},
withdraw(amount) {
if (amount > balance) return 'Insufficient funds';
balance -= amount;
return balance;
},
checkBalance() {
return balance;
}
};
}
const account = secureBankAccount(1000);
console.log(account.deposit(500)); // 1500
console.log(account.checkBalance()); // 1500
3. What are the differences between == and === in JavaScript?
Answer:
==(loose equality) compares values after type coercion.===(strict equality) compares both value and type without coercion.
Example:
console.log(5 == '5'); // true → type coercion happens
console.log(5 === '5'); // false → different types
console.log(null == undefined); // true
console.log(null === undefined); // false
console.log(true == 1); // true
console.log(true === 1); // false
Best Practice: Always use === to avoid unpredictable coercion behavior unless there’s a specific reason to use ==.
4. How does prototypal inheritance work in JavaScript?
Answer:
JavaScript uses prototypal inheritance, meaning objects can inherit properties and methods from other objects through a prototype chain.
Concept:
Every object in JavaScript has an internal [[Prototype]], which is a reference to another object. When accessing a property, the engine first looks in the object itself. If not found, it traverses up the prototype chain.
Example:
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
return `${this.name} makes a sound`;
};
const dog = new Animal('Rex');
console.log(dog.speak()); // Rex makes a sound
The object dog inherits the speak() method from Animal.prototype.
Modern Syntax (ES6):
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound`;
}
}
5. What is hoisting in JavaScript?
Answer:
Hoisting is JavaScript’s default behavior of moving declarations to the top of their scope before code execution.
Rules:
- Variables declared with
varare hoisted and initialized withundefined. letandconstare hoisted but are in a temporal dead zone until initialized.- Function declarations are fully hoisted with their definitions.
Examples:
console.log(a); // undefined
var a = 5;
console.log(b); // ReferenceError
let b = 10;
Functions:
greet(); // Hello!
function greet() {
console.log('Hello!');
}
But:
greet(); // TypeError
var greet = function () {
console.log('Hello!');
};
Here, greet is hoisted as undefined, so calling it as a function throws an error.
6. How does the this keyword behave in different contexts?
Answer:
The value of this is determined by how a function is invoked, not how it’s defined.
Scenarios:
- Global scope:
thisrefers to the global object (windowin browsers). - Function (non-strict mode):
thisis the global object. - Function (strict mode):
thisisundefined. - Method call:
thisis the object before the dot. - Arrow functions:
thisis lexically bound (inherits from parent scope). - Constructors:
thisrefers to the new object being created.
Examples:
function globalFunc() {
console.log(this); // window or global object
}
const obj = {
name: 'Alice',
method: function () {
return this.name;
},
arrow: () => {
return this.name;
}
};
console.log(obj.method()); // 'Alice'
console.log(obj.arrow()); // undefined (inherits from global scope)
7. Explain the differences between call, apply, and bind.
Answer:
All three methods are used to invoke functions with a specific this context.
call: Invokes the function immediately, passing arguments individually.apply: Invokes the function immediately, passing arguments as an array.bind: Returns a new function withthispermanently bound; does not invoke it immediately.
Example:
function greet(greeting, punctuation) {
return `${greeting}, ${this.name}${punctuation}`;
}
const person = { name: 'Jane' };
console.log(greet.call(person, 'Hello', '!')); // Hello, Jane!
console.log(greet.apply(person, ['Hi', '.'])); // Hi, Jane.
const boundGreet = greet.bind(person, 'Hey');
console.log(boundGreet('?')); // Hey, Jane?
8. What is the difference between synchronous and asynchronous code in JavaScript?
Answer:
- Synchronous code runs sequentially and blocks further execution until the current operation completes.
- Asynchronous code allows the engine to continue executing while waiting for operations (like I/O, timers) to finish.
Synchronous Example:
console.log('1');
console.log('2');
console.log('3');
Asynchronous Example:
console.log('A');
setTimeout(() => {
console.log('B');
}, 1000);
console.log('C');
Output:
A
C
B
This is made possible by the Event Loop, which defers async callbacks until the stack is clear.
9. What are Promises and how do they help with asynchronous code?
Answer:
A Promise is an object that represents the eventual result (success or failure) of an asynchronous operation. It offers a cleaner alternative to callbacks, improving code readability and maintainability.
States of a Promise:
pendingfulfilledrejected
Example:
const fetchData = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Data loaded');
}, 2000);
});
fetchData
.then(data => console.log(data))
.catch(err => console.error(err));
Promises allow chaining and centralized error handling.
10. Explain async/await and how it differs from using .then().
Answer:
async/await is syntactic sugar over Promises that allows writing asynchronous code in a synchronous-looking manner.
asyncfunctions always return a Promise.awaitpauses execution insideasyncfunctions until the Promise settles.
Example using async/await:
async function loadData() {
try {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
console.log(data);
} catch (err) {
console.error('Error:', err);
}
}
Equivalent using .then():
fetch('https://api.example.com/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
While both approaches are valid, async/await is generally easier to read, especially when dealing with multiple asynchronous operations in sequence.
11. What are the different ways to create objects in JavaScript?
Answer:
In JavaScript, there are several ways to create objects, each with its use cases and implications regarding inheritance and performance.
1. Object Literal:
The most straightforward way:
const person = {
name: 'John',
age: 30,
greet() {
return `Hello, I'm ${this.name}`;
}
};
2. Object Constructor (Using new Object()):
const person = new Object();
person.name = 'John';
person.age = 30;
3. Constructor Function:
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function () {
return `Hi, I'm ${this.name}`;
};
}
const person1 = new Person('Alice', 25);
4. Object.create():
Creates a new object with the specified prototype object.
const proto = {
greet() {
return `Hello, ${this.name}`;
}
};
const person = Object.create(proto);
person.name = 'Bob';
5. ES6 Class Syntax:
Provides syntactic sugar over constructor functions and prototypes.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hello, I'm ${this.name}`;
}
}
const person = new Person('Sam', 40);
Each method affects the object’s prototype and inheritance chain differently. For example, Object.create() gives fine-grained control over inheritance, while class and constructor functions are better suited for reusable blueprints.
12. What is the difference between deep and shallow copying in JavaScript?
Answer:
Copying in JavaScript can be either shallow or deep, depending on whether nested objects are also copied.
Shallow Copy: Duplicates only the first level; nested objects are still referenced.
const original = { name: 'John', details: { age: 30 } };
const shallowCopy = { ...original };
shallowCopy.name = 'Jane';
shallowCopy.details.age = 40;
console.log(original.details.age); // 40 (mutated!)
Deep Copy: Recursively duplicates all levels, ensuring that nested objects are not referenced.
Methods to Perform Deep Copy:
- JSON methods (basic, but limited):
const deepCopy = JSON.parse(JSON.stringify(original));
Limitations: Cannot copy functions, undefined, Map, Set, circular references.
- Using structuredClone (modern browsers):
const deepCopy = structuredClone(original);
- Manual recursive copy or utility libraries like Lodash:
import cloneDeep from 'lodash/cloneDeep';
const deepCopy = cloneDeep(original);
Shallow copies are suitable for flat objects, whereas deep copies are essential for preserving data immutability in nested structures.
13. What are generators in JavaScript and how are they different from regular functions?
Answer:
A generator is a special kind of function that can pause execution and resume later, maintaining its internal state between calls.
Syntax:
function* generatorFunction() {
yield 'A';
yield 'B';
return 'C';
}
Execution:
const gen = generatorFunction();
console.log(gen.next()); // { value: 'A', done: false }
console.log(gen.next()); // { value: 'B', done: false }
console.log(gen.next()); // { value: 'C', done: true }
Key Differences from Regular Functions:
- Pausing & Resuming: Generators use
yieldto pause andnext()to resume. - Iterability: Generators are iterable and can be used in
for...ofloops. - State Preservation: Retain execution context between yields.
- Use Cases: Lazily evaluated sequences, asynchronous control flows, infinite data streams.
Example: Generating an infinite ID sequence
function* idGenerator() {
let id = 1;
while (true) {
yield id++;
}
}
const gen = idGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
Generators enable writing complex iterative logic more elegantly than regular functions.
14. What is currying in JavaScript?
Answer:
Currying is a functional programming technique where a function with multiple parameters is transformed into a sequence of functions, each taking a single parameter.
Curried Function:
function multiply(a) {
return function (b) {
return a * b;
};
}
const double = multiply(2);
console.log(double(5)); // 10
Using Arrow Functions:
const multiply = a => b => a * b;
Use Cases:
- Creating function factories.
- Reducing code duplication.
- Better reusability in functional pipelines.
Example: Currying for configuration
const greet = salutation => name => `${salutation}, ${name}`;
const sayHello = greet('Hello');
console.log(sayHello('Alice')); // Hello, Alice
Currying enables partial application and enhances code modularity.
15. What is memoization and how can it be implemented in JavaScript?
Answer:
Memoization is an optimization technique where expensive function calls are cached based on their input arguments, so subsequent calls with the same inputs return the cached result.
Manual Implementation:
function memoize(fn) {
const cache = {};
return function (...args) {
const key = JSON.stringify(args);
if (key in cache) return cache[key];
const result = fn(...args);
cache[key] = result;
return result;
};
}
Use Case Example:
const factorial = memoize(function (n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
});
console.log(factorial(5)); // 120
console.log(factorial(5)); // Cached
Memoization is especially useful in dynamic programming, recursion, and pure functions where inputs always yield the same outputs.
16. Explain the difference between Object.freeze() and Object.seal().
Answer:
Both Object.freeze() and Object.seal() prevent modifications to an object, but to different extents.
Object.freeze():
- Makes the object immutable.
- Cannot add, delete, or modify properties.
writable: false,configurable: false
const obj = Object.freeze({ name: 'Alice' });
obj.name = 'Bob'; // No effect
obj.age = 30; // No effect
Object.seal():
- Prevents adding or deleting properties.
- Existing properties can still be modified.
configurable: false,writable: true
const obj = Object.seal({ name: 'Alice' });
obj.name = 'Bob'; // Works
delete obj.name; // Fails
obj.age = 30; // No effect
Use freeze for full immutability, and seal when you want a fixed structure but mutable values.
17. How does JavaScript handle memory management and garbage collection?
Answer:
JavaScript uses automatic memory management, meaning the developer doesn’t manually allocate or deallocate memory. The garbage collector reclaims memory that’s no longer reachable.
Key Concepts:
- Reachability: Values are retained in memory if they are reachable from the root (typically the global object).
- Garbage Collection Strategies:
- Mark-and-Sweep: Common algorithm that marks reachable objects and sweeps away unmarked ones.
- Reference Counting (less common): Tracks how many references an object has; if none, it can be deleted.
Memory Leak Scenarios:
- Unreferenced variables in closures.
- Forgotten timers/intervals.
- Detached DOM nodes.
- Global variables.
Example:
function createLeak() {
let largeArray = new Array(1000000).fill('leak');
return function () {
console.log(largeArray[0]);
};
}
Here, the closure may prevent garbage collection of largeArray.
Developers must manually dereference long-lived objects in certain architectures to avoid leaks.
18. What is the module pattern in JavaScript and how does it promote encapsulation?
Answer:
The module pattern is a design pattern that uses closures to encapsulate private variables and expose only a public API. It was commonly used before ES6 modules.
Structure:
const Module = (function () {
let privateVar = 'I am private';
function privateMethod() {
return privateVar;
}
return {
publicMethod() {
return privateMethod();
}
};
})();
console.log(Module.publicMethod()); // I am private
Benefits:
- Prevents polluting the global namespace.
- Offers data privacy.
- Provides a clean API.
With ES6, import/export achieves similar results using actual modules.
19. What are higher-order functions in JavaScript?
Answer:
A higher-order function is a function that either:
- Takes another function as an argument, or
- Returns a function.
Higher-order functions are a cornerstone of functional programming and are widely used in JavaScript.
Examples:
function repeat(n, action) {
for (let i = 0; i < n; i++) {
action(i);
}
}
repeat(3, console.log); // Logs 0, 1, 2
Common Higher-Order Functions:
mapfilterreduceforEach
Function Returning Function:
function greeter(greeting) {
return function (name) {
return `${greeting}, ${name}`;
};
}
const sayHi = greeter('Hi');
console.log(sayHi('Alice')); // Hi, Alice
Higher-order functions promote code reuse, abstraction, and declarative style.
20. What are the differences between var, let, and const?
Answer:
| Feature | var |
let |
const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting | Yes (initialized as undefined) |
Yes (TDZ) | Yes (TDZ) |
| Redeclaration | Allowed | Not allowed | Not allowed |
| Reassignment | Allowed | Allowed | Not allowed |
Example:
function testScope() {
if (true) {
var x = 1;
let y = 2;
const z = 3;
}
console.log(x); // 1
// console.log(y); // ReferenceError
// console.log(z); // ReferenceError
}
var is function-scoped and hoisted unsafely, while let and const are block-scoped and safer for modern development. Use const by default unless you plan to reassign.
21. What are the key differences between function declarations and function expressions in JavaScript?
Answer:
In JavaScript, functions can be created in two primary ways: function declarations and function expressions. While both result in function objects, there are important differences regarding hoisting, naming, and usage.
Function Declaration:
A function declared with the function keyword at the top level of a script or inside a block.
function greet() {
return 'Hello';
}
- Hoisted entirely, including the function body.
- Can be invoked before it appears in the code.
sayHi(); // 'Hi'
function sayHi() {
console.log('Hi');
}
Function Expression:
A function assigned to a variable, which may be anonymous or named.
const greet = function () {
return 'Hello';
};
- Not hoisted with its body. Only the variable is hoisted, initialized as
undefined. - Cannot be used before its definition.
sayHello(); // TypeError: sayHello is not a function
const sayHello = function () {
console.log('Hello');
};
Named Function Expression:
const factorial = function fact(n) {
return n <= 1 ? 1 : n * fact(n - 1);
};
Named expressions are useful for recursion within expressions and better stack traces in debuggers.
22. How do default parameters work in JavaScript functions?
Answer:
Default parameters allow function parameters to have default values if no value or undefined is passed during invocation.
Syntax:
function greet(name = 'Guest') {
return `Hello, ${name}`;
}
console.log(greet()); // Hello, Guest
console.log(greet('John')); // Hello, John
Key Behavior:
- Only
undefinedtriggers the default. Passingnull,false, or0uses those values.
function example(a = 5) {
return a;
}
console.log(example()); // 5
console.log(example(undefined)); // 5
console.log(example(null)); // null
You can also use other parameters as defaults:
function total(price, tax = price * 0.1) {
return price + tax;
}
console.log(total(100)); // 110
Default parameters simplify handling optional arguments and reduce the need for manual checks.
23. What is destructuring and how can it be used effectively?
Answer:
Destructuring is a syntax that allows extracting values from arrays or properties from objects into distinct variables.
Object Destructuring:
const user = { name: 'Alice', age: 25 };
const { name, age } = user;
console.log(name); // Alice
Array Destructuring:
const numbers = [1, 2, 3];
const [first, second] = numbers;
console.log(first); // 1
Nested Destructuring:
const person = {
name: 'Bob',
address: {
city: 'New York',
zip: 10001
}
};
const {
address: { city }
} = person;
console.log(city); // New York
With Default Values:
const { role = 'User' } = {};
console.log(role); // User
Function Parameter Destructuring:
function display({ name, age }) {
console.log(`${name} is ${age}`);
}
display({ name: 'Tom', age: 30 });
Destructuring improves readability and eliminates repetitive access patterns.
24. How does the spread operator differ from the rest operator?
Answer:
Though syntactically similar (...), the spread and rest operators serve opposite purposes.
Spread Operator: Expands an iterable (like an array or object) into individual elements.
const arr1 = [1, 2];
const arr2 = [...arr1, 3, 4];
console.log(arr2); // [1, 2, 3, 4]
Object Spread:
const user = { name: 'John' };
const updatedUser = { ...user, age: 30 };
Rest Operator: Gathers remaining parameters or properties into a new array or object.
In Function Parameters:
function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sum(1, 2, 3)); // 6
In Destructuring:
const { a, ...rest } = { a: 1, b: 2, c: 3 };
console.log(rest); // { b: 2, c: 3 }
The spread operator is used for expansion, while the rest operator is used for collection.
25. What are symbols in JavaScript and when should they be used?
Answer:
A Symbol is a primitive and immutable data type introduced in ES6, used to create unique identifiers for object properties.
const sym = Symbol('description');
Properties:
- Symbols are always unique.
- They are not enumerable in
for...inloops orObject.keys().
Use Case: Creating Non-Colliding Property Keys
const sym1 = Symbol('id');
const obj = {
[sym1]: 123,
name: 'Alice'
};
console.log(obj[sym1]); // 123
Global Symbol Registry:
const globalSym = Symbol.for('app.id');
const sameGlobal = Symbol.for('app.id');
console.log(globalSym === sameGlobal); // true
Symbols are ideal for hiding internal object properties or implementing private fields in libraries.
26. What are tagged template literals in JavaScript?
Answer:
Tagged template literals allow you to process template strings with a function. It’s a powerful feature for custom string interpolation and DSLs.
Syntax:
function tag(strings, ...values) {
console.log(strings); // Literal parts
console.log(values); // Interpolated values
}
const name = 'Alice';
const age = 30;
tag`My name is ${name} and I am ${age} years old.`;
Example: HTML Sanitization
function escapeHTML(strings, ...values) {
return strings.reduce((acc, str, i) => {
const val = String(values[i] || '')
.replace(/</g, '<')
.replace(/>/g, '>');
return acc + str + val;
}, '');
}
const userInput = '<script>alert("XSS")</script>';
const result = escapeHTML`User input: ${userInput}`;
console.log(result); // Escaped output
Tagged templates are useful in security, localization, formatting, and templating engines.
27. What are WeakMap and WeakSet, and how do they differ from Map and Set?
Answer:
WeakMap and WeakSet are memory-sensitive collections where keys (or values) must be objects and are held weakly (non-preventing garbage collection).
WeakMap:
const wm = new WeakMap();
let obj = {};
wm.set(obj, 'value');
console.log(wm.get(obj)); // 'value'
obj = null; // Entry is removed from WeakMap when object is garbage collected
WeakSet:
const ws = new WeakSet();
let user = { name: 'Alice' };
ws.add(user);
console.log(ws.has(user)); // true
user = null; // Now eligible for GC
Key Differences:
| Feature | WeakMap | Map |
|---|---|---|
| Keys | Objects only | Any type |
| GC Support | Yes (weak references) | No |
| Iterability | Not iterable | Iterable |
Weak collections are useful for caching and storing metadata where automatic cleanup is needed.
28. What is the difference between call stack and task queue in JavaScript?
Answer:
Both the call stack and task queue (also known as the callback queue) are part of the JavaScript runtime environment that help manage synchronous and asynchronous code.
Call Stack:
- Manages function invocations in a LIFO (Last In, First Out) manner.
- Executes synchronous code immediately.
Task Queue:
- Stores asynchronous callbacks (e.g., from
setTimeout, DOM events). - Executed only when the call stack is empty.
Example:
console.log('A');
setTimeout(() => {
console.log('B');
}, 0);
console.log('C');
Output:
A
C
B
Why?
- ‘A’ and ‘C’ are pushed to the stack and executed.
- The
setTimeoutcallback goes into the task queue. - The event loop waits for the call stack to empty before pushing the task to the stack.
Understanding this flow is critical to mastering asynchronous JavaScript behavior.
29. What are microtasks and how are they different from macrotasks?
Answer:
JavaScript divides asynchronous tasks into microtasks and macrotasks, which differ in when they’re executed relative to the call stack.
Microtasks:
- Processed immediately after the current script or task finishes and before the next macrotask.
- Examples:
Promise.then,MutationObserver,queueMicrotask
Macrotasks:
- Scheduled for the next event loop cycle.
- Examples:
setTimeout,setInterval,I/O events,setImmediate(Node.js)
Example:
console.log('start');
setTimeout(() => {
console.log('macrotask');
}, 0);
Promise.resolve().then(() => {
console.log('microtask');
});
console.log('end');
Output:
start
end
microtask
macrotask
Execution Order:
- Synchronous code (
start,end) - Microtasks (
microtask) - Macrotasks (
macrotask)
Microtasks are prioritized to allow more fine-grained control over task execution and UI rendering.
30. What is the temporal dead zone (TDZ) in JavaScript?
Answer:
The temporal dead zone (TDZ) is the time between entering the scope of a let or const variable and its actual declaration, during which the variable cannot be accessed.
Accessing the variable during the TDZ results in a ReferenceError.
Example:
{
// TDZ starts
// console.log(x); // ReferenceError
let x = 10; // TDZ ends
console.log(x); // 10
}
Why It Exists:
- Enforces cleaner coding.
- Prevents accidental usage of uninitialized variables.
Key Notes:
- Only applies to
letandconst. vardoes not have a TDZ—it’s hoisted and initialized asundefined.
The TDZ helps developers avoid subtle bugs caused by hoisting and encourages predictable variable behavior.
31. What is the difference between immutable and mutable data structures in JavaScript?
Answer:
In JavaScript, mutable data structures can be changed after they are created, while immutable data structures cannot be changed—any modification results in a new copy rather than an in-place update.
Mutable Data:
- Objects and arrays are mutable by default.
- Changes affect the original structure and any references to it.
const arr = [1, 2, 3];
arr.push(4);
console.log(arr); // [1, 2, 3, 4]
Immutable Data:
- Strings and primitive types are immutable.
- Libraries like Immer, Immutable.js, or using spread syntax help achieve immutability in complex structures.
const arr = [1, 2, 3];
const newArr = [...arr, 4];
console.log(newArr); // [1, 2, 3, 4]
console.log(arr); // [1, 2, 3]
Benefits of Immutability:
- Easier to track changes.
- Better performance in diffing algorithms (e.g., React).
- Safer in functional and concurrent programming.
32. What is the significance of Immediately Invoked Function Expressions (IIFEs) in JavaScript?
Answer:
An Immediately Invoked Function Expression (IIFE) is a function that is executed immediately after its definition. It’s used to create a private scope and avoid polluting the global namespace.
Syntax:
(function () {
console.log('IIFE executed');
})();
Or with arrow functions:
(() => {
console.log('Arrow IIFE');
})();
Why Use IIFEs?
- Data encapsulation: Keeps variables local to the function.
- Avoids global scope pollution.
- Legacy module pattern before ES6 modules.
Example:
const counter = (function () {
let count = 0;
return {
increment() {
return ++count;
},
decrement() {
return --count;
}
};
})();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
The enclosed count variable is inaccessible from outside, ensuring encapsulation.
33. What is the difference between Object.assign() and the spread operator for object cloning?
Answer:
Both Object.assign() and the spread operator (...) are used to copy properties from one object to another. However, there are subtle differences.
Object.assign():
const original = { a: 1 };
const clone = Object.assign({}, original);
- Copies enumerable own properties.
- Performs a shallow copy.
Spread Operator:
const original = { a: 1 };
const clone = { ...original };
- Also performs a shallow copy.
- Cleaner and more readable syntax.
- Cannot copy property descriptors or prototypes.
Differences:
| Feature | Object.assign() | Spread Operator |
|---|---|---|
| Syntax | Verbose | Concise |
| Symbol Properties | Not copied | Not copied |
| Inheritance | Only own properties | Only own properties |
| Shallow Copy | Yes | Yes |
Use Case with Nested Objects (Both are Shallow):
const obj = { a: { b: 1 } };
const shallowClone = { ...obj };
shallowClone.a.b = 2;
console.log(obj.a.b); // 2 (still linked!)
Use deep cloning methods for nested structures (e.g., structuredClone, lodash.cloneDeep).
34. How does JavaScript handle asynchronous iteration with for await...of?
Answer:
for await...of is a loop introduced in ES2018 that allows asynchronous iteration over objects that conform to the AsyncIterable protocol.
It pauses on each iteration until the Promise resolves, making it ideal for sequential async operations.
Use Case:
async function* fetchData() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}
(async () => {
for await (let value of fetchData()) {
console.log(value);
}
})();
Output:
1
2
3
When to Use:
- Reading streams (
ReadableStream) - Paginated API calls
- Rate-limited operations
Unlike .then() or Promise.all(), it allows granular control over asynchronous iteration with built-in backpressure handling.
35. What are JavaScript decorators and how do they work?
Answer:
Decorators are a stage-3 JavaScript proposal (not officially standardized yet) that provide a way to modify or annotate classes and class members (methods, properties, accessors) using reusable functions.
Syntax:
@decorator
class MyClass {}
Or on methods:
class MyClass {
@log
greet() {
return 'Hello';
}
}
Implementation:
function log(target, key, descriptor) {
const original = descriptor.value;
descriptor.value = function (...args) {
console.log(`Calling ${key} with`, args);
return original.apply(this, args);
};
return descriptor;
}
Use Cases:
- Logging
- Caching
- Authorization
- Dependency injection
Decorators are heavily used in frameworks like Angular and NestJS, and require Babel/TypeScript with proper config to work.
36. What is function throttling and debouncing in JavaScript?
Answer:
Both throttling and debouncing are techniques to limit the rate at which a function is executed, especially in response to events like scrolling, resizing, or keystrokes.
Throttling:
Executes a function at most once every specified interval.
function throttle(fn, limit) {
let lastCall = 0;
return function (...args) {
const now = Date.now();
if (now - lastCall >= limit) {
lastCall = now;
fn.apply(this, args);
}
};
}
Debouncing:
Delays execution until after a pause in activity.
function debounce(fn, delay) {
let timeout;
return function (...args) {
clearTimeout(timeout);
timeout = setTimeout(() => fn.apply(this, args), delay);
};
}
Use Cases:
- Throttle: scroll events, API polling.
- Debounce: search input, auto-saving.
Using these improves performance and prevents overwhelming the browser or server.
37. What are proxies in JavaScript and how can they be used?
Answer:
A Proxy is an object that wraps another object or function and intercepts fundamental operations (e.g., reading/writing properties, function calls) using traps.
Syntax:
const handler = {
get(target, prop) {
return prop in target ? target[prop] : 'Property not found';
}
};
const obj = { name: 'Alice' };
const proxy = new Proxy(obj, handler);
console.log(proxy.name); // Alice
console.log(proxy.age); // Property not found
Common Traps:
getsethasdeletePropertyapply(for functions)
Use Cases:
- Validation
- Logging
- Access control
- Mocking APIs
- Reactive data (Vue.js uses proxies internally)
Proxies enable metaprogramming by customizing low-level object behavior dynamically.
38. How does optional chaining (?.) work in JavaScript?
Answer:
Optional chaining allows safe access to deeply nested object properties without checking each level manually.
Syntax:
const user = {
profile: {
name: 'Tom'
}
};
console.log(user?.profile?.name); // 'Tom'
console.log(user?.address?.city); // undefined
Without Optional Chaining:
if (user && user.address && user.address.city) {
console.log(user.address.city);
}
With Optional Chaining:
console.log(user?.address?.city); // Much cleaner
Works With:
- Object properties
- Function calls:
obj.method?.() - Arrays:
arr?.[0]
This prevents runtime errors when accessing undefined or null values and makes code more concise and safer.
39. What is dynamic import in JavaScript and when is it used?
Answer:
Dynamic import (import()) is a way to load ES modules on demand rather than at compile time. It returns a Promise and enables code-splitting and lazy loading.
Syntax:
import('./math.js')
.then(module => {
console.log(module.add(2, 3));
});
Use Cases:
- On-demand loading of large modules
- Conditional imports
- Route-based code splitting in frameworks like React
Example with async/await:
async function loadMath() {
const math = await import('./math.js');
console.log(math.subtract(5, 2));
}
Dynamic imports are ideal for optimizing load times and memory usage in large applications.
40. What is the Reflect API in JavaScript?
Answer:
The Reflect API is a built-in JavaScript object that provides methods for interceptable JavaScript operations. It complements the Proxy API and provides default behavior for Proxy traps.
Example:
const obj = { name: 'Alice' };
console.log(Reflect.get(obj, 'name')); // 'Alice'
Reflect.set(obj, 'age', 30);
console.log(obj.age); // 30
Key Methods:
Reflect.get()Reflect.set()Reflect.has()Reflect.deleteProperty()Reflect.ownKeys()
Use Cases:
- Uniform method interface for meta-operations.
- Used in Proxy handlers to apply default behavior.
- Safer and more predictable than equivalent object operations.
Reflect simplifies writing meta-level logic and helps in instrumenting or observing JavaScript code behavior.
41. How does setTimeout behave inside a loop and how can it be fixed?
Answer:
When using setTimeout inside a loop, especially with var, developers often encounter unexpected results due to JavaScript’s asynchronous behavior and variable scoping.
Problem Example (with var):
for (var i = 1; i <= 3; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
Output:
4
4
4
This happens because setTimeout is asynchronous and the loop completes before the callbacks execute. Since var is function-scoped, all closures reference the same variable, which ends at i = 4.
Solutions:
1. Using let (block-scoped):
for (let i = 1; i <= 3; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
Output:
1
2
3
2. Using IIFE:
for (var i = 1; i <= 3; i++) {
(function (j) {
setTimeout(() => {
console.log(j);
}, 1000);
})(i);
}
The IIFE captures the current value of i and preserves it inside each closure.
42. What is tail call optimization and does JavaScript support it?
Answer:
Tail Call Optimization (TCO) is an optimization where the JavaScript engine reuses the stack frame for a recursive function if the recursive call is the last action (tail position), preventing stack overflow.
Tail Call (Optimizable):
function factorial(n, acc = 1) {
if (n <= 1) return acc;
return factorial(n - 1, n * acc); // tail call
}
If optimized, this avoids building up new stack frames.
Non-Tail Call (Not Optimizable):
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1); // not tail call
}
Support:
- TCO is part of ES6 but not implemented in most engines, including Chrome and Node.js.
- Only strict mode functions are eligible for TCO.
- Currently, Safari supports it partially.
Tail call optimization is essential in languages with heavy recursion like Scheme, but in JavaScript, it remains mostly theoretical.
43. How does JavaScript handle equality with Object.is() compared to == and ===?
Answer:
Object.is() is a method introduced in ES6 to compare two values with more accuracy than == or ===.
Differences:
| Comparison | Behavior |
|---|---|
== |
Loose equality with coercion |
=== |
Strict equality without coercion |
Object.is() |
Like === but handles +0 vs -0 and NaN correctly |
Examples:
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true
console.log(+0 === -0); // true
console.log(Object.is(+0, -0)); // false
Object.is() is useful when strict and predictable equality is needed, especially for edge cases.
44. What is the purpose of Function.prototype.bind() and how does it work?
Answer:
The bind() method creates a new function with a permanently bound this context and optional pre-set arguments.
Syntax:
function greet(greeting, punctuation) {
return `${greeting}, ${this.name}${punctuation}`;
}
const person = { name: 'Alice' };
const greetPerson = greet.bind(person, 'Hello');
console.log(greetPerson('!')); // Hello, Alice!
Characteristics:
- Returns a new function (doesn’t invoke it).
- Useful for event handlers and callbacks where
thisis lost. - Cannot be overridden by
call()orapply()once bound.
Partial Application Example:
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
console.log(double(5)); // 10
bind() is essential for functional programming patterns and event delegation in class components.
45. What are the new features introduced in ES2020?
Answer:
ES2020 introduced several enhancements to JavaScript, improving syntax, functionality, and developer ergonomics.
Key Features:
- BigInt:
Handles integers beyond the safe Number limit.
const large = 9007199254740991n + 1n;
console.log(large); // 9007199254740992n
- Dynamic Import:
Loads modules on demand.
import('./module.js').then(module => module.init());
- Nullish Coalescing (
??):
Returns the right-hand side only if the left-hand side is null or undefined.
const name = null ?? 'Guest'; // 'Guest'
- Optional Chaining (
?.):
Safe property access.
user?.profile?.email;
- Promise.allSettled():
Waits for all promises to settle (fulfilled or rejected).
Promise.allSettled([Promise.resolve(1), Promise.reject('Error')])
.then(results => console.log(results));
- globalThis:
Universal global object reference.
console.log(globalThis.setTimeout); // Works in all environments
These features make JavaScript more robust and concise across all environments.
46. How do you make a class property truly private in JavaScript?
Answer:
JavaScript introduced private class fields in ES2022 using the # syntax, which enforces true privacy—fields are not accessible outside the class body.
Example:
class Counter {
#count = 0;
increment() {
this.#count++;
return this.#count;
}
getCount() {
return this.#count;
}
}
const c = new Counter();
console.log(c.increment()); // 1
console.log(c.#count); // SyntaxError
Benefits:
- Not accessible via
this['#count']or any form of bracket notation. - Cannot be tampered with or inspected externally.
Before #, privacy was simulated using closures or WeakMap, but this new syntax enforces it natively and securely.
47. What is event delegation and why is it useful?
Answer:
Event delegation is a pattern where a single event listener is added to a parent element to handle events from its children using event bubbling.
Benefits:
- Fewer event listeners = better performance.
- Dynamically added elements are automatically handled.
Example:
document.getElementById('list').addEventListener('click', function (event) {
if (event.target.tagName === 'LI') {
console.log('Item clicked:', event.target.textContent);
}
});
Here, even if new <li> elements are added to #list, the single listener on the parent still handles them.
It is commonly used in list rendering, dynamic UIs, and table interactions.
48. What is the difference between localStorage, sessionStorage, and cookies?
Answer:
| Feature | localStorage |
sessionStorage |
cookies |
|---|---|---|---|
| Lifespan | Until manually cleared | Until tab/browser closes | Custom expiration |
| Capacity | ~5–10MB | ~5–10MB | ~4KB |
| Scope | Same-origin | Tab-specific, same-origin | Sent with every HTTP request |
| Accessibility | JS only | JS only | JS + Server |
Usage:
localStorage.setItem('key', 'value');
sessionStorage.setItem('key', 'value');
document.cookie = 'key=value; expires=...';
- localStorage: Persistent client-side storage.
- sessionStorage: Session-limited storage.
- cookies: Server-client communication; suitable for authentication tokens.
Each has its use case depending on security, persistence, and network needs.
49. How does Object.defineProperty() work and what can it control?
Answer:
Object.defineProperty() allows fine-grained control over object property behavior via descriptors.
Syntax:
Object.defineProperty(obj, 'prop', {
value: 42,
writable: false,
enumerable: true,
configurable: false
});
Descriptor Options:
value: actual value.writable: can be changed.enumerable: visible in loops.configurable: can be deleted or modified.
Example:
const user = {};
Object.defineProperty(user, 'id', {
value: 1,
writable: false,
enumerable: false,
configurable: false
});
console.log(user.id); // 1
user.id = 2;
console.log(user.id); // Still 1
It’s commonly used in libraries and frameworks to create hidden or read-only properties.
50. What is the arguments object and how does it differ from rest parameters?
Answer:
The arguments object is an array-like object available inside regular functions that contains all passed arguments.
Example:
function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
Limitations:
- Not available in arrow functions.
- Not a real array (no
map,reduce, etc.). - Doesn’t work well with default/rest parameters.
Rest Parameters (...args):
function sum(...args) {
return args.reduce((a, b) => a + b);
}
Differences:
| Feature | arguments |
...args (rest) |
|---|---|---|
| Type | Array-like | True array |
| Arrow function | Not available | Available |
| Use with defaults | Not supported well | Fully supported |
Prefer rest parameters for cleaner, more modern, and predictable code.
51. What is the difference between map(), forEach(), filter(), and reduce() in JavaScript?
Answer:
These are all array methods in JavaScript used for iteration and transformation, but each serves a distinct purpose.
forEach()
- Executes a function on each array element.
- Does not return a value.
const arr = [1, 2, 3];
arr.forEach(num => console.log(num));
Use case: Performing side effects (e.g., logging, modifying external variables).
map()
- Transforms each element and returns a new array.
- Does not mutate the original array.
const doubled = [1, 2, 3].map(num => num * 2);
console.log(doubled); // [2, 4, 6]
Use case: Creating a new array of transformed data.
filter()
- Returns a new array with elements that satisfy a condition.
const evens = [1, 2, 3, 4].filter(num => num % 2 === 0);
console.log(evens); // [2, 4]
Use case: Selecting a subset of data.
reduce()
- Applies a function to accumulate a single value from an array.
const total = [1, 2, 3, 4].reduce((acc, val) => acc + val, 0);
console.log(total); // 10
Use case: Summing, product calculation, or flattening arrays.
Each method is designed for specific functional operations and helps maintain clean, immutable, and declarative code.
52. What is the difference between synchronous and asynchronous iterators?
Answer:
Synchronous iterators work with data that is immediately available. Asynchronous iterators handle data that arrives over time (e.g., streams or API responses).
Synchronous Iterator:
const array = [1, 2, 3];
for (const value of array) {
console.log(value);
}
- Uses
Symbol.iterator. - Suitable for finite, immediately available collections.
Asynchronous Iterator:
async function* asyncGenerator() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
}
for await (const value of asyncGenerator()) {
console.log(value);
}
- Uses
Symbol.asyncIterator. - Supports
for await...of. - Ideal for streaming, paginated APIs, or lazy loading.
Asynchronous iterators allow controlled consumption of async data, often seen in file processing, HTTP streaming, and event handling.
53. How does the event loop handle promises vs setTimeout()?
Answer:
JavaScript separates asynchronous tasks into microtasks and macrotasks.
- Promises (via
.then,catch,finally) are queued as microtasks. setTimeout()is a macrotask.
Microtasks are executed right after the current synchronous execution finishes, and before any macrotasks.
Example:
console.log('Start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
Output:
Start
End
Promise
setTimeout
This ordering ensures microtasks (e.g., promises) run before setTimeout, even with a delay of 0 ms. This model avoids starvation and helps prioritize shorter, lighter tasks.
54. What are async generators and how do they differ from regular generators?
Answer:
Async generators combine async functions with generators, allowing await within the generator body and enabling asynchronous iteration using for await...of.
Async Generator:
async function* asyncNumbers() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
}
Consumption:
(async () => {
for await (const num of asyncNumbers()) {
console.log(num);
}
})();
Key Differences from Regular Generators:
| Feature | Regular Generator | Async Generator |
|---|---|---|
| Yield Values | Synchronous | Awaited async values |
| Consumption | for...of |
for await...of |
| Return Type | { value, done } |
Promise that resolves to {} |
Async generators are ideal for lazy-loading async data sources such as paginated APIs or file readers.
55. What is the Intl object in JavaScript and what are its uses?
Answer:
The Intl object is a built-in internationalization API introduced in ECMAScript to provide language-sensitive string comparison, number formatting, date/time formatting, and more.
Common Uses:
1. Number Formatting:
const number = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
console.log(number.format(2500)); // $2,500.00
2. Date Formatting:
const date = new Intl.DateTimeFormat('de-DE').format(new Date());
console.log(date); // e.g., 01.06.2025
3. Collation (Sorting):
const collator = new Intl.Collator('sv');
console.log(['z', 'ä', 'å'].sort(collator.compare)); // ['z', 'ä', 'å']
Intl enhances apps for localization without relying on external libraries.
56. What is the difference between typeof and instanceof?
Answer:
Both are used to inspect types, but they serve different purposes.
typeof:
Returns a string representing the type of a primitive or function.
console.log(typeof 'abc'); // "string"
console.log(typeof 42); // "number"
console.log(typeof []); // "object"
- Works well for primitives.
- Not reliable for arrays or custom objects.
instanceof:
Checks whether an object is an instance of a specific constructor or class.
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
- Works with classes and inheritance.
- Returns false for primitives.
Combined Use:
function isString(value) {
return typeof value === 'string' || value instanceof String;
}
Use typeof for primitives, instanceof for objects, and Array.isArray() for arrays.
57. What is the purpose of queueMicrotask()?
Answer:
queueMicrotask() queues a microtask to be executed after the current script and before the next macrotask.
It behaves like .then() from a resolved Promise, but avoids Promise creation overhead.
Example:
console.log('Start');
queueMicrotask(() => {
console.log('Microtask');
});
console.log('End');
Output:
Start
End
Microtask
Use Cases:
- Running low-priority operations soon after current logic.
- Avoiding
setTimeoutdelays. - Handling logic after DOM changes or reflows.
It gives fine-grained control over execution timing with minimal performance impact.
58. What is function composition and how is it implemented in JavaScript?
Answer:
Function composition is the process of combining multiple functions into a single function where the output of one becomes the input of the next.
Manual Implementation:
const compose = (...fns) => x => fns.reduceRight((v, fn) => fn(v), x);
Example:
const double = x => x * 2;
const square = x => x * x;
const composed = compose(square, double);
console.log(composed(3)); // square(double(3)) => square(6) => 36
Why Use It?
- Encourages modularity and reusability.
- Common in functional programming.
- Reduces side effects and increases readability.
Used heavily in libraries like Redux for middleware chaining.
59. What is the difference between strict mode and sloppy mode?
Answer:
Strict mode is a restricted variant of JavaScript that removes some silent errors and imposes stricter parsing and error handling.
Enable Strict Mode:
'use strict';
Key Differences:
| Feature | Sloppy Mode | Strict Mode |
|---|---|---|
| Implicit globals | Allowed | Disallowed (throws error) |
| Assigning to read-only | Silently fails | Throws error |
| Duplicate parameters | Allowed | Syntax error |
this in functions |
Global object | undefined |
Example:
function sloppy() {
x = 10; // no error
}
'use strict';
function strict() {
x = 10; // ReferenceError
}
Strict mode promotes secure, predictable, and optimized code execution.
60. How do modules work in JavaScript?
Answer:
JavaScript supports modules to allow encapsulation, reuse, and dependency management across files.
ES6 Modules:
- Use
importandexportstatements. - Static structure (resolved at compile time).
- Each module has its own scope.
Export:
export const name = 'Alice';
export function greet() {
return 'Hello';
}
Import:
import { name, greet } from './module.js';
Default Export:
export default function () {
return 'Default';
}
import myFunc from './module.js';
Benefits:
- Lazy loading
- Tree shaking (dead code elimination)
- Encapsulation and clean APIs
Modules are the foundation for modern JavaScript development workflows, frameworks, and bundlers.
61. What is a memory leak in JavaScript and how can it be avoided?
Answer:
A memory leak in JavaScript occurs when memory that is no longer needed is not released. This can lead to increased memory usage and eventually degrade performance or crash the application.
Common Causes of Memory Leaks:
- Uncleared timers and intervals:
let id = setInterval(() => {
// Never cleared
}, 1000);
- Detached DOM elements:
let div = document.createElement('div');
document.body.appendChild(div);
document.body.removeChild(div);
// Still referenced
const reference = div;
- Global variables:
function leak() {
leaky = 'I leak'; // Implicit global
}
- Closures holding large objects:
function outer() {
const bigData = new Array(1000000).fill('x');
return function inner() {
console.log(bigData[0]);
};
}
Prevention Techniques:
- Use
let/constto avoid accidental globals. - Clear intervals/timeouts when done.
- Nullify references when no longer needed.
- Use weak references where applicable (
WeakMap,WeakSet). - Monitor memory using browser dev tools (Performance tab).
Effective memory management ensures long-term performance and scalability.
62. What are service workers and how do they enhance web applications?
Answer:
A service worker is a script that runs in the background, separate from the main browser thread. It acts as a proxy between your web app and the network, enabling offline experiences, background sync, and push notifications.
Key Features:
- Intercepts network requests and can serve custom responses.
- Can cache assets and API responses (offline-first strategies).
- Runs independently of the webpage (like a daemon).
Registration Example:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(() => {
console.log('Service Worker Registered');
});
}
sw.js Example:
self.addEventListener('install', event => {
event.waitUntil(
caches.open('static-v1').then(cache => {
return cache.addAll(['/index.html', '/styles.css']);
})
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(res => {
return res || fetch(event.request);
})
);
});
Service workers are fundamental to Progressive Web Apps (PWAs) and allow modern web apps to behave like native apps.
63. How does hoisting work with classes in JavaScript?
Answer:
In JavaScript, class declarations are hoisted but not initialized. This means you cannot access a class before its declaration in the code—attempting to do so results in a ReferenceError.
Example:
const user = new User(); // ReferenceError: Cannot access 'User' before initialization
class User {
constructor() {
this.name = 'Alice';
}
}
Unlike function declarations, which are hoisted and can be used before their declaration, classes are hoisted into the temporal dead zone (TDZ) like let and const.
Class expressions are not hoisted at all:
const User = class {
constructor() {
this.name = 'Bob';
}
};
Class hoisting is designed to enforce cleaner and more predictable object-oriented code.
64. What is a singleton pattern in JavaScript and how do you implement it?
Answer:
A singleton pattern ensures that a class has only one instance and provides a global point of access to that instance.
Simple Implementation:
const Singleton = (function () {
let instance;
function createInstance() {
return { name: 'SingletonInstance' };
}
return {
getInstance() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
const obj1 = Singleton.getInstance();
const obj2 = Singleton.getInstance();
console.log(obj1 === obj2); // true
ES6 Class Singleton:
class Singleton {
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
this.id = Math.random();
Singleton.instance = this;
}
}
const a = new Singleton();
const b = new Singleton();
console.log(a === b); // true
Singletons are commonly used in configuration, caching, logging, and global state management.
65. What are template literals and how do they enhance string manipulation?
Answer:
Template literals are string literals introduced in ES6 that allow embedded expressions, multi-line strings, and easier interpolation.
Syntax:
const name = 'John';
const greeting = `Hello, ${name}!`;
console.log(greeting); // Hello, John!
Features:
- Multi-line strings:
const multiline = `This is
a multiline
string.`;
- Embedded expressions:
const a = 5;
const b = 10;
console.log(`Sum: ${a + b}`); // Sum: 15
- Tagged templates: Used for DSLs and custom processing.
function tag(strings, ...values) {
return strings[0] + values[0].toUpperCase();
}
console.log(tag`Hello, ${'world'}`); // Hello, WORLD
Template literals significantly improve readability and flexibility in string operations.
66. What is the new.target meta-property in JavaScript?
Answer:
new.target is a meta-property introduced in ES6 that allows you to detect whether a function or constructor was called using the new operator.
Example:
function Person() {
if (!new.target) {
throw new Error('Must use new');
}
this.name = 'Alice';
}
const p = new Person(); // works
const p2 = Person(); // throws
In Class Constructors:
class Animal {
constructor() {
console.log(new.target.name);
}
}
class Dog extends Animal {}
new Animal(); // Animal
new Dog(); // Dog
new.target is useful for enforcing constructor usage and base class initialization checks.
67. How do call(), apply(), and bind() differ in behavior?
Answer:
All three methods allow you to explicitly set the this context of a function. However, they differ in how and when they invoke the function.
call(thisArg, ...args)
Immediately calls the function with thisArg and individual arguments.
function greet(greeting) {
return `${greeting}, ${this.name}`;
}
const user = { name: 'Alice' };
console.log(greet.call(user, 'Hello')); // Hello, Alice
apply(thisArg, [args])
Same as call, but arguments are passed as an array.
console.log(greet.apply(user, ['Hi'])); // Hi, Alice
bind(thisArg, ...args)
Returns a new function with thisArg bound. Does not invoke immediately.
const sayHi = greet.bind(user, 'Hey');
console.log(sayHi()); // Hey, Alice
bind() is useful for callbacks and event handlers where context is needed later.
68. What is the difference between throw and Promise.reject()?
Answer:
Both are used to signal an error, but they behave differently depending on context.
throw
- Synchronously throws an error.
- Interrupts control flow unless caught.
try {
throw new Error('Sync Error');
} catch (e) {
console.error(e.message); // Sync Error
}
Promise.reject()
- Returns a rejected promise.
- Error is handled in
.catch()or withawait+try/catch.
Promise.reject('Async Error')
.catch(err => console.log(err));
Inside async function:
async function example() {
throw new Error('Thrown Error'); // Rejected
// OR
// return Promise.reject('Manual reject');
}
Use throw in synchronous code and Promise.reject() for programmatically returning rejected promises.
69. What are mixins in JavaScript and how are they implemented?
Answer:
Mixins are a pattern for adding reusable behavior to classes or objects without using classical inheritance.
Object-based Mixin:
const canEat = {
eat() {
console.log('eating');
}
};
const canWalk = {
walk() {
console.log('walking');
}
};
const person = Object.assign({}, canEat, canWalk);
person.eat(); // eating
Class-based Mixin:
const canFly = Base => class extends Base {
fly() {
console.log('flying');
}
};
class Animal {}
class Bird extends canFly(Animal) {}
const b = new Bird();
b.fly(); // flying
Mixins promote code reuse and composition over inheritance, making systems more modular and maintainable.
70. How do web workers work in JavaScript?
Answer:
Web Workers allow JavaScript to run in the background on a separate thread, enabling non-blocking computation-intensive tasks.
Creating a Worker:
worker.js
self.onmessage = function (e) {
const result = e.data * 2;
self.postMessage(result);
};
main.js
const worker = new Worker('worker.js');
worker.postMessage(5);
worker.onmessage = function (e) {
console.log('Result:', e.data); // 10
};
Features:
- Runs in a separate thread.
- Cannot access DOM directly.
- Useful for heavy computations (image processing, sorting).
Web Workers improve responsiveness and performance in single-threaded environments like browsers.
71. What are the differences between Promise.all(), Promise.allSettled(), Promise.race(), and Promise.any()?
Answer:
These are Promise combinators used to work with multiple Promises in parallel. They each differ in how they aggregate results and handle rejections.
Promise.all()
- Resolves when all Promises succeed.
- Rejects immediately if any Promise rejects.
Promise.all([Promise.resolve(1), Promise.resolve(2)])
.then(console.log); // [1, 2]
Promise.all([Promise.resolve(1), Promise.reject('Error')])
.catch(console.error); // 'Error'
Promise.allSettled()
- Resolves when all Promises settle (either fulfilled or rejected).
- Never rejects.
Promise.allSettled([
Promise.resolve('OK'),
Promise.reject('Fail')
]).then(console.log);
/*
[
{ status: 'fulfilled', value: 'OK' },
{ status: 'rejected', reason: 'Fail' }
]
*/
Promise.race()
- Resolves or rejects as soon as one Promise settles, with that result.
Promise.race([
new Promise(resolve => setTimeout(() => resolve('First'), 100)),
new Promise(resolve => setTimeout(() => resolve('Second'), 200))
]).then(console.log); // 'First'
Promise.any() (ES2021)
- Resolves with the first fulfilled Promise.
- Rejects only if all Promises reject.
Promise.any([
Promise.reject('Fail'),
Promise.resolve('Success')
]).then(console.log); // 'Success'
Each method provides different strategies for managing concurrency depending on success/failure tolerance.
72. What are event bubbling and capturing in the DOM?
Answer:
When an event occurs in the DOM, it goes through three phases:
- Capturing Phase – from the root to the target.
- Target Phase – event reaches the target element.
- Bubbling Phase – event bubbles up from the target to the root.
Event Bubbling (default):
document.getElementById('child').addEventListener('click', () => {
console.log('Child clicked');
});
document.getElementById('parent').addEventListener('click', () => {
console.log('Parent clicked');
});
Clicking the child logs:
Child clicked
Parent clicked
Event Capturing:
To handle events during the capturing phase, pass { capture: true }:
document.getElementById('parent').addEventListener(
'click',
() => console.log('Captured at parent'),
{ capture: true }
);
Stop Propagation:
event.stopPropagation(); // Prevents bubbling or capturing beyond this point
Understanding event flow is crucial for designing responsive and maintainable UIs.
73. What are pure functions and why are they important?
Answer:
A pure function is a function that:
- Always returns the same output for the same input.
- Has no side effects (does not modify global state, DOM, or variables outside its scope).
Example of Pure Function:
function add(a, b) {
return a + b;
}
Impure Function:
let total = 0;
function addToTotal(a) {
total += a;
}
Benefits of Pure Functions:
- Predictable and testable.
- Easier to debug.
- Safer for concurrency and parallelism.
- Ideal for functional programming and immutability.
Pure functions form the foundation of Redux, React components, and many functional patterns in JavaScript.
74. What are observables and how do they differ from Promises?
Answer:
Observables are push-based collections of multiple future values, commonly used in reactive programming (e.g., RxJS).
Promise:
- Eager, single value.
- Not cancellable.
- Executes once.
const promise = new Promise(resolve => {
setTimeout(() => resolve('Done'), 1000);
});
Observable:
- Lazy, multiple values over time.
- Supports cancellation and operators.
- Used with
subscribe().
import { Observable } from 'rxjs';
const obs = new Observable(subscriber => {
subscriber.next(1);
subscriber.next(2);
subscriber.complete();
});
obs.subscribe({
next: val => console.log(val),
complete: () => console.log('Done')
});
Output:
1
2
Done
Observables are ideal for streams, UI events, and real-time data.
75. What are tagged template literals used for in JavaScript?
Answer:
Tagged template literals allow you to preprocess a template string with a function, called a tag function. They’re powerful tools for DSLs, localization, security, and formatting.
Syntax:
function tag(strings, ...values) {
return strings[0] + values.map((v, i) => v.toUpperCase() + strings[i + 1]).join('');
}
const name = 'alice';
console.log(tag`Hello, ${name}!`); // Hello, ALICE!
Use Cases:
- Escaping HTML
- Localization
- Syntax highlighting
- Code generation
Tagged templates give you control over string interpolation and rendering logic.
76. How does JavaScript handle modules in Node.js vs in the browser?
Answer:
Modules are handled differently in Node.js (CommonJS) vs the browser (ES Modules).
Node.js (CommonJS):
- Uses
require()andmodule.exports. - Synchronous and file-based.
// module.js
module.exports = function () { return 'Hello'; };
// main.js
const greet = require('./module');
Browser (ESM):
- Uses
importandexport. - Asynchronous and must be declared with
type="module".
// module.js
export function greet() {
return 'Hello';
}
// main.js
import { greet } from './module.js';
Differences:
| Feature | CommonJS (Node.js) | ESM (Browser/Modern Node) |
|---|---|---|
| Import Syntax | require() |
import/export |
| Execution | Synchronous | Asynchronous |
Top-level await |
Not supported | Supported (in modules) |
Node.js now supports both with .mjs extensions or type: "module" in package.json.
77. What is destructuring and what problems does it solve?
Answer:
Destructuring is a convenient way of extracting values from arrays or properties from objects into variables.
Array Destructuring:
const [a, b] = [1, 2];
console.log(a, b); // 1 2
Object Destructuring:
const user = { name: 'Tom', age: 30 };
const { name } = user;
Use Cases:
- Simplifies data access from complex objects.
- Clean syntax in function parameters.
- Enables default values and renaming.
function greet({ name = 'Guest' }) {
return `Hello, ${name}`;
}
Destructuring improves code readability and reduces boilerplate.
78. How does JavaScript handle time and date manipulation?
Answer:
JavaScript uses the built-in Date object to handle time and dates.
Creation:
const now = new Date();
const fromString = new Date('2025-06-01');
Getters and Setters:
now.getFullYear(); // 2025
now.setMonth(5); // June (zero-indexed)
Formatting:
now.toISOString(); // '2025-06-01T10:00:00.000Z'
now.toLocaleDateString('en-GB'); // '01/06/2025'
Alternatives:
- Use Date-fns or Luxon for better APIs.
- Avoid moment.js (now deprecated in favor of modern libraries).
Handling time zones, durations, and daylight saving shifts can be complex, hence external libraries are often preferred.
79. What is the difference between shallow copy and deep copy?
Answer:
- Shallow copy duplicates only the first level of an object.
- Deep copy recursively copies all nested objects and arrays.
Shallow Copy:
const original = { name: 'Alice', nested: { age: 30 } };
const copy = { ...original };
copy.nested.age = 40;
console.log(original.nested.age); // 40 (linked)
Deep Copy:
const deep = JSON.parse(JSON.stringify(original));
Better Method:
const cloneDeep = require('lodash.clonedeep');
const deepClone = cloneDeep(original);
Use deep copy when immutability and data isolation are necessary, especially in state management.
80. How does JavaScript handle floating-point arithmetic and precision?
Answer:
JavaScript uses the IEEE 754 standard for floating-point arithmetic, which can lead to precision issues.
Problem:
console.log(0.1 + 0.2); // 0.30000000000000004
Solution:
Use rounding or libraries to handle accurate decimal arithmetic.
function round(num, precision = 2) {
return Math.round(num * 10 ** precision) / 10 ** precision;
}
console.log(round(0.1 + 0.2)); // 0.3
Libraries:
decimal.jsbig.jsbignumber.js
These libraries handle arbitrary-precision decimals accurately, which is critical in finance and scientific applications.
71. What are the differences between Promise.all(), Promise.allSettled(), Promise.race(), and Promise.any()?
Answer:
These are Promise combinators used to work with multiple Promises in parallel. They each differ in how they aggregate results and handle rejections.
Promise.all()
- Resolves when all Promises succeed.
- Rejects immediately if any Promise rejects.
Promise.all([Promise.resolve(1), Promise.resolve(2)])
.then(console.log); // [1, 2]
Promise.all([Promise.resolve(1), Promise.reject('Error')])
.catch(console.error); // 'Error'
Promise.allSettled()
- Resolves when all Promises settle (either fulfilled or rejected).
- Never rejects.
Promise.allSettled([
Promise.resolve('OK'),
Promise.reject('Fail')
]).then(console.log);
/*
[
{ status: 'fulfilled', value: 'OK' },
{ status: 'rejected', reason: 'Fail' }
]
*/
Promise.race()
- Resolves or rejects as soon as one Promise settles, with that result.
Promise.race([
new Promise(resolve => setTimeout(() => resolve('First'), 100)),
new Promise(resolve => setTimeout(() => resolve('Second'), 200))
]).then(console.log); // 'First'
Promise.any() (ES2021)
- Resolves with the first fulfilled Promise.
- Rejects only if all Promises reject.
Promise.any([
Promise.reject('Fail'),
Promise.resolve('Success')
]).then(console.log); // 'Success'
Each method provides different strategies for managing concurrency depending on success/failure tolerance.
72. What are event bubbling and capturing in the DOM?
Answer:
When an event occurs in the DOM, it goes through three phases:
- Capturing Phase – from the root to the target.
- Target Phase – event reaches the target element.
- Bubbling Phase – event bubbles up from the target to the root.
Event Bubbling (default):
document.getElementById('child').addEventListener('click', () => {
console.log('Child clicked');
});
document.getElementById('parent').addEventListener('click', () => {
console.log('Parent clicked');
});
Clicking the child logs:
Child clicked
Parent clicked
Event Capturing:
To handle events during the capturing phase, pass { capture: true }:
document.getElementById('parent').addEventListener(
'click',
() => console.log('Captured at parent'),
{ capture: true }
);
Stop Propagation:
event.stopPropagation(); // Prevents bubbling or capturing beyond this point
Understanding event flow is crucial for designing responsive and maintainable UIs.
73. What are pure functions and why are they important?
Answer:
A pure function is a function that:
- Always returns the same output for the same input.
- Has no side effects (does not modify global state, DOM, or variables outside its scope).
Example of Pure Function:
function add(a, b) {
return a + b;
}
Impure Function:
let total = 0;
function addToTotal(a) {
total += a;
}
Benefits of Pure Functions:
- Predictable and testable.
- Easier to debug.
- Safer for concurrency and parallelism.
- Ideal for functional programming and immutability.
Pure functions form the foundation of Redux, React components, and many functional patterns in JavaScript.
74. What are observables and how do they differ from Promises?
Answer:
Observables are push-based collections of multiple future values, commonly used in reactive programming (e.g., RxJS).
Promise:
- Eager, single value.
- Not cancellable.
- Executes once.
const promise = new Promise(resolve => {
setTimeout(() => resolve('Done'), 1000);
});
Observable:
- Lazy, multiple values over time.
- Supports cancellation and operators.
- Used with
subscribe().
import { Observable } from 'rxjs';
const obs = new Observable(subscriber => {
subscriber.next(1);
subscriber.next(2);
subscriber.complete();
});
obs.subscribe({
next: val => console.log(val),
complete: () => console.log('Done')
});
Output:
1
2
Done
Observables are ideal for streams, UI events, and real-time data.
75. What are tagged template literals used for in JavaScript?
Answer:
Tagged template literals allow you to preprocess a template string with a function, called a tag function. They’re powerful tools for DSLs, localization, security, and formatting.
Syntax:
function tag(strings, ...values) {
return strings[0] + values.map((v, i) => v.toUpperCase() + strings[i + 1]).join('');
}
const name = 'alice';
console.log(tag`Hello, ${name}!`); // Hello, ALICE!
Use Cases:
- Escaping HTML
- Localization
- Syntax highlighting
- Code generation
Tagged templates give you control over string interpolation and rendering logic.
76. How does JavaScript handle modules in Node.js vs in the browser?
Answer:
Modules are handled differently in Node.js (CommonJS) vs the browser (ES Modules).
Node.js (CommonJS):
- Uses
require()andmodule.exports. - Synchronous and file-based.
// module.js
module.exports = function () { return 'Hello'; };
// main.js
const greet = require('./module');
Browser (ESM):
- Uses
importandexport. - Asynchronous and must be declared with
type="module".
// module.js
export function greet() {
return 'Hello';
}
// main.js
import { greet } from './module.js';
Differences:
| Feature | CommonJS (Node.js) | ESM (Browser/Modern Node) |
|---|---|---|
| Import Syntax | require() |
import/export |
| Execution | Synchronous | Asynchronous |
Top-level await |
Not supported | Supported (in modules) |
Node.js now supports both with .mjs extensions or type: "module" in package.json.
77. What is destructuring and what problems does it solve?
Answer:
Destructuring is a convenient way of extracting values from arrays or properties from objects into variables.
Array Destructuring:
const [a, b] = [1, 2];
console.log(a, b); // 1 2
Object Destructuring:
const user = { name: 'Tom', age: 30 };
const { name } = user;
Use Cases:
- Simplifies data access from complex objects.
- Clean syntax in function parameters.
- Enables default values and renaming.
function greet({ name = 'Guest' }) {
return `Hello, ${name}`;
}
Destructuring improves code readability and reduces boilerplate.
78. How does JavaScript handle time and date manipulation?
Answer:
JavaScript uses the built-in Date object to handle time and dates.
Creation:
const now = new Date();
const fromString = new Date('2025-06-01');
Getters and Setters:
now.getFullYear(); // 2025
now.setMonth(5); // June (zero-indexed)
Formatting:
now.toISOString(); // '2025-06-01T10:00:00.000Z'
now.toLocaleDateString('en-GB'); // '01/06/2025'
Alternatives:
- Use Date-fns or Luxon for better APIs.
- Avoid moment.js (now deprecated in favor of modern libraries).
Handling time zones, durations, and daylight saving shifts can be complex, hence external libraries are often preferred.
79. What is the difference between shallow copy and deep copy?
Answer:
- Shallow copy duplicates only the first level of an object.
- Deep copy recursively copies all nested objects and arrays.
Shallow Copy:
const original = { name: 'Alice', nested: { age: 30 } };
const copy = { ...original };
copy.nested.age = 40;
console.log(original.nested.age); // 40 (linked)
Deep Copy:
const deep = JSON.parse(JSON.stringify(original));
Better Method:
const cloneDeep = require('lodash.clonedeep');
const deepClone = cloneDeep(original);
Use deep copy when immutability and data isolation are necessary, especially in state management.
80. How does JavaScript handle floating-point arithmetic and precision?
Answer:
JavaScript uses the IEEE 754 standard for floating-point arithmetic, which can lead to precision issues.
Problem:
console.log(0.1 + 0.2); // 0.30000000000000004
Solution:
Use rounding or libraries to handle accurate decimal arithmetic.
function round(num, precision = 2) {
return Math.round(num * 10 ** precision) / 10 ** precision;
}
console.log(round(0.1 + 0.2)); // 0.3
Libraries:
decimal.jsbig.jsbignumber.js
These libraries handle arbitrary-precision decimals accurately, which is critical in finance and scientific applications.
91. How do JavaScript’s logical operators (||, &&, ??) behave differently?
Answer:
JavaScript provides several logical operators that short-circuit and return operands based on truthiness:
|| (Logical OR)
- Returns the first truthy value, or the last value if none are truthy.
const result = null || 0 || 'hello';
console.log(result); // 'hello'
- Commonly used to set default values (pre-ES2020):
const name = userInput || 'Guest';
&& (Logical AND)
- Returns the first falsy value, or the last if all are truthy.
const result = 'hi' && 0 && 'bye';
console.log(result); // 0
- Often used for conditional execution:
isLoggedIn && showDashboard();
?? (Nullish Coalescing, ES2020)
- Returns the right-hand value only if the left-hand is
nullorundefined.
const value = null ?? 'default'; // 'default'
const zero = 0 ?? 42; // 0
Use ?? instead of || when falsy values like 0, '', or false are meaningful.
92. What is a throttle function and how does it differ from debounce?
Answer:
Throttle and debounce are performance techniques used to limit how often a function executes.
Throttle
- Executes the function at most once in a specified time period.
function throttle(fn, limit) {
let inThrottle;
return function (...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
Use Case: Scroll, window resize, mouse movement.
Debounce
- Delays the function until after the user has stopped triggering it.
function debounce(fn, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
Use Case: Search inputs, form validation, auto-saving.
| Feature | Throttle | Debounce |
|---|---|---|
| Execution | At intervals | After delay inactivity |
| Purpose | Control frequency | Avoid repeated calls |
93. What is the role of Object.entries(), Object.values(), and Object.keys()?
Answer:
These static methods allow iteration over object properties in different forms:
Object.keys(obj)
- Returns an array of the object’s own enumerable property names.
Object.keys({ a: 1, b: 2 }); // ['a', 'b']
Object.values(obj)
- Returns an array of the object’s own enumerable property values.
Object.values({ a: 1, b: 2 }); // [1, 2]
Object.entries(obj)
- Returns an array of
[key, value]pairs.
Object.entries({ a: 1, b: 2 }); // [['a', 1], ['b', 2]]
Use Cases:
- Looping:
for (const [key, value] of Object.entries(obj)) {
console.log(`${key}: ${value}`);
}
These methods simplify object inspection, transformation, and iteration.
94. What is a generator function and how does it work?
Answer:
A generator function is a special type of function that can pause and resume its execution using the yield keyword.
Syntax:
function* count() {
yield 1;
yield 2;
yield 3;
}
Usage:
const gen = count();
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 }
Characteristics:
- Lazy evaluation.
- Useful for managing large or infinite data sets.
- Ideal for implementing custom iterators.
Generators simplify async workflows (when combined with yield* and recursion) and support pull-based data processing.
95. What is the difference between == null and === null?
Answer:
== null
- Returns true for both
nullandundefineddue to type coercion.
console.log(null == null); // true
console.log(undefined == null); // true
=== null
- Strict equality; returns true only for
null.
console.log(null === null); // true
console.log(undefined === null); // false
Best Practice:
- Use
x == nullto check for bothnullandundefined. - Use
===when type precision is required.
Example:
if (value == null) {
// value is either null or undefined
}
This is a concise way to guard against both types of “empty” values.
96. What is optional chaining and how does it prevent runtime errors?
Answer:
Optional chaining (?.) allows you to safely access deeply nested properties without throwing an error if a reference is null or undefined.
Example:
const user = { profile: null };
console.log(user.profile?.name); // undefined (no error)
With Functions:
user.getName?.(); // Executes only if getName exists
With Arrays:
const arr = null;
console.log(arr?.[0]); // undefined
Benefits:
- Prevents
Cannot read property 'x' of undefined. - Cleaner than chaining with
&&.
Without Optional Chaining:
user && user.profile && user.profile.name;
Optional chaining is now widely supported and improves readability and safety.
97. What is the globalThis object and when should it be used?
Answer:
globalThis provides a unified reference to the global object across environments (browser, Node.js, Web Workers).
Before globalThis:
// Browser
window === this;
// Node.js
global === this;
This inconsistency made cross-platform scripts difficult to write.
With globalThis:
console.log(globalThis.setTimeout === setTimeout); // true
Use Cases:
- Accessing globals reliably.
- Creating cross-platform libraries.
- Polyfilling global methods.
globalThis is the standard, environment-independent global object.
98. What is an Immediately Invoked Function Expression (IIFE) and why is it used?
Answer:
An IIFE is a function that runs as soon as it is defined. It helps create a new scope and avoid polluting the global namespace.
Syntax:
(function () {
console.log('IIFE ran');
})();
Arrow Function IIFE:
(() => {
console.log('Arrow IIFE');
})();
Use Cases:
- Encapsulation.
- Module pattern (pre-ES6).
- Private variable scope.
Example with Private Data:
const counter = (function () {
let count = 0;
return {
increment: () => ++count,
get: () => count
};
})();
console.log(counter.increment()); // 1
IIFEs are a foundational concept in modular JavaScript design.
99. How does JavaScript handle errors and exceptions?
Answer:
JavaScript uses try...catch...finally blocks for handling exceptions. Errors can be thrown manually or raised by the engine.
Syntax:
try {
// risky code
} catch (error) {
console.error(error.message);
} finally {
// always runs
}
Throwing Custom Errors:
throw new Error('Something went wrong');
Error Types:
ReferenceErrorTypeErrorSyntaxErrorRangeErrorEvalError
Try-Catch with Async:
async function fetchData() {
try {
const res = await fetch('/api/data');
const data = await res.json();
} catch (e) {
console.error('Fetch failed', e);
}
}
Handling errors properly improves robustness and user experience.
100. What is the module pattern and how does it help organize code?
Answer:
The module pattern encapsulates private and public methods using closures or ES6 modules, promoting separation of concerns and maintainability.
Classic IIFE Module Pattern:
const Calculator = (function () {
let total = 0;
function add(x) {
total += x;
}
function getTotal() {
return total;
}
return {
add,
getTotal
};
})();
ES6 Module:
// calc.js
let total = 0;
export function add(x) {
total += x;
}
export function getTotal() {
return total;
}
// app.js
import { add, getTotal } from './calc.js';
Benefits:
- Encapsulation of private state.
- Reusability and testability.
- Improved code structure and maintenance.
The module pattern was a precursor to ES6 modules and remains relevant in many JS codebases.
Conclusion
Mastering JavaScript at an advanced level goes far beyond understanding syntax and basic functions—it requires a deep comprehension of its execution model, memory management, asynchronous paradigms, modern features like ES Modules, generators, proxies, and best practices in design patterns and architecture. This exhaustive list of 100 advanced JavaScript intelligence interview questions has been carefully curated to help developers sharpen their skills, clarify ambiguities, and prepare for real-world technical interviews and challenges.
From examining the intricacies of the event loop and closures to exploring the nuanced behaviors of this, Symbol.iterator, garbage collection, and concurrency control mechanisms like Promise.all and async/await, this guide ensures you’re equipped not just to answer tough questions—but to understand the why behind JavaScript’s core design principles.
We’ve also dived into important topics that are increasingly in demand in large-scale systems: modularization, throttling/debouncing, immutability, WeakRefs, observables, and optimization strategies used by engines like V8. Understanding these advanced mechanisms not only strengthens your chances of succeeding in high-level interviews, but also enables you to build more performant, scalable, and maintainable applications.
This guide is brought to you in partnership with DigitalDefynd, a trusted platform committed to helping learners and professionals stay on the cutting edge of technology. Whether you’re preparing for your next big role or simply refining your knowledge as a senior engineer or team lead, DigitalDefynd’s curated resources are designed to accelerate your learning and career trajectory.
Stay tuned for more advanced content and keep building, breaking, and mastering JavaScript.