The most common JavaScript interview questions with detailed answers. Filter by difficulty, search by keyword, and link to interactive explanations.
Showing 126 of 126 questions
Global scope (accessible everywhere), Function scope (variables declared inside functions with var), and Block scope (variables declared with let/const inside curly braces).
Learn more →Yes! This is called the scope chain. Inner scopes can access outer scope variables, but outer scopes cannot access inner scope variables.
Learn more →undefined. var declarations are hoisted and initialized with undefined, so x exists but has no value yet.
Learn more →ReferenceError: Cannot access 'y' before initialization. let is hoisted but not initialized - this is the Temporal Dead Zone.
Learn more →Only function declarations. Function declarations are fully hoisted. Function expressions (including arrow functions) cannot be called before their definition - they follow variable hoisting rules.
Learn more →map transforms each element and returns a new array. forEach executes a function for each element for side effects and returns undefined. Use map when you need the resulting array.
Learn more →undefined. This is important to handle in your code.
Learn more →indexOf searches for a specific value using strict equality. findIndex searches using a predicate function, useful for finding objects by property values.
Learn more →slice(start, end) extracts a portion without mutating the original. splice(start, deleteCount, ...items) modifies the original array by removing/inserting elements and returns removed elements.
Learn more →Shallow copy: [...arr], arr.slice(), or Array.from(arr). Deep copy: structuredClone(arr) or JSON.parse(JSON.stringify(arr)) for JSON-serializable data.
Learn more →sort() converts elements to strings by default. "10" comes before "2" in lexicographic order. Use nums.sort((a, b) => a - b) for numeric sorting.
Learn more →Use spread: [...arr, newElement] for end, [newElement, ...arr] for beginning, or [...arr.slice(0, i), element, ...arr.slice(i)] for middle. ES2023: arr.toSpliced(i, 0, element).
Learn more →A closure is a function that has access to variables from its outer scope even after the outer function has returned. It "remembers" the environment where it was created.
Learn more →Use Object.create(proto): const child = Object.create(parent); This creates a new object with parent as its prototype.
Learn more →hasOwnProperty checks if the property exists directly on the object (not inherited). The in operator checks the entire prototype chain, including inherited properties.
Learn more →A LIFO data structure that tracks function execution. Functions are pushed when called, popped when they return. JavaScript has one call stack because it is single-threaded.
Learn more →Infinite or very deep recursion without proper base cases. Each function call adds to the stack, and when it exceeds the maximum size, a RangeError is thrown.
Learn more →No. JavaScript is single-threaded with one call stack. However, the runtime environment (browser/Node) provides Web APIs that operate independently, allowing asynchronous behavior without blocking the main thread.
Learn more →A callback is a function passed as an argument to another function, which is then invoked inside the outer function to complete some kind of action.
Learn more →Synchronous callbacks execute immediately during the function execution (like array methods). Asynchronous callbacks execute later, after some operation completes (like setTimeout, fetch, event handlers).
Learn more →Callback hell (pyramid of doom) is when multiple asynchronous operations are nested within callbacks, creating deeply indented code that is hard to read and maintain.
Learn more →Using the Promise constructor: new Promise((resolve, reject) => { ... }). Call resolve(value) for success, reject(error) for failure.
Learn more →No. .finally() does not receive any arguments. It is used for cleanup code that needs to run regardless of the Promise outcome.
Learn more →In modern ES2022+ modules, yes (top-level await). In older JavaScript or non-modules, no - await must be inside an async function.
Learn more →Use try/catch blocks. When a Promise rejects, await throws an exception that can be caught. The catch block receives the rejection reason.
Learn more →Use Promise.all() with an array of Promises. Start all operations, then await the Promise.all() which resolves when all complete.
Learn more →Use Promise.allSettled when partial success is acceptable — for example, loading a dashboard where some widgets can fail independently. Use Promise.all when all results are required and any single failure should abort the operation.
Learn more →It uses an IIFE to create a function scope. Variables declared inside the IIFE are not accessible from outside. The IIFE returns an object with methods that close over those variables, providing controlled access through a public API.
Learn more →ES modules are evaluated once on first import. Subsequent imports return the cached module object. This means any variable declared at module scope exists as a single instance shared by all importers.
Learn more →It decouples object creation from usage. The calling code works with a shared interface and does not need to know which concrete class or constructor is used, making the system easier to extend and maintain.
Learn more →addEventListener is the browser's implementation of the Observer Pattern. The DOM element is the subject, and each event handler is an observer. When the event fires, all registered handlers are notified synchronously.
Learn more →Strategy encapsulates each algorithm as a separate function or object, making them independently testable and swappable. If/else branches hardcode behavior inside the function, making it harder to extend and test individual paths.
Learn more →A higher-order function that takes a function and returns a new function with added behavior IS a decorator. The wrapping function preserves the original interface while adding responsibilities like logging, caching, or validation.
Learn more →When an inner scope declares a variable with the same name as an outer scope, the inner variable "shadows" (hides) the outer one within that scope.
Learn more →Yes! They ARE hoisted to the top of their block scope. However, they are not initialized, creating the Temporal Dead Zone from the start of the block to the declaration line.
Learn more →The time between entering a scope and the variable declaration line where a let/const variable exists but cannot be accessed. Accessing it throws a ReferenceError.
Learn more →function foo(){} is a declaration - fully hoisted (can call before). var foo = function(){} is an expression - hoisted as undefined, so calling before throws TypeError.
Learn more →Arrow functions follow variable hoisting rules, not function declaration rules. If declared with const/let, they are in TDZ until declaration.
Learn more →The period between entering a scope and the variable declaration line where a let/const variable exists but cannot be accessed. Accessing it throws a ReferenceError.
Learn more →Lexical scoping means scope is determined by where variables/functions are declared in the source code (at write time), not where they are called (at runtime). The scope chain is static.
Learn more →JavaScript follows the scope chain: it first looks in the current scope, then the outer scope, continuing up until it reaches the global scope. If not found, it throws ReferenceError.
Learn more →push/pop are O(1) - constant time at the end. shift/unshift are O(n) - must reindex all elements when adding/removing at the beginning.
Learn more →sort() converts elements to strings by default. "10" comes before "2" lexicographically. Use sort((a, b) => a - b) for numeric sorting.
Learn more →No. forEach always iterates through all elements. Use a regular for loop, for...of, or some()/every() if you need early termination.
Learn more →array.reduce((acc, item) => { const key = item[keyProp]; if (!acc[key]) acc[key] = []; acc[key].push(item); return acc; }, {})
Learn more →TypeError: Reduce of empty array with no initial value. Always provide an initial value to avoid this.
Learn more →Use reduce with an object as accumulator: arr.reduce((acc, word) => { acc[word] = (acc[word] || 0) + 1; return acc; }, {})
Learn more →Objects are compared by reference, not value. {a: 1} === {a: 1} is false because they are different objects in memory. Use find() with a predicate to search by property values.
Learn more →In comparator, check primary field first. If equal, check secondary: users.sort((a, b) => { if (a.age !== b.age) return a.age - b.age; return a.name.localeCompare(b.name); })
Learn more →Shallow copy creates new array but references same nested objects. Deep copy creates new array AND new nested objects. [...arr] is shallow. structuredClone(arr) creates deep copy.
Learn more →A closure is created when a function is defined inside another function. The inner function maintains a reference to the outer scope at the time of its definition.
Learn more →Use closures. Define variables in an outer function, return an object with methods that access those variables. The variables are hidden from external code but accessible to the closure methods.
Learn more →An IIFE (Immediately Invoked Function Expression) that returns an object with public methods. Private variables and functions are hidden in the closure. (function() { var private = 1; return { public: function() {} } })()
Learn more →Output: 3, 3, 3. Because var is function-scoped, all closures share the same i. By the time setTimeout runs, the loop has finished and i is 3.
Learn more →1) Use let instead of var (block-scoped, new binding per iteration). 2) Use an IIFE to capture current value. 3) Use forEach which creates new scope each iteration.
Learn more →Yes. Closures keep references to outer scope variables, preventing garbage collection. If closures capture large objects or DOM references and are kept alive indefinitely (e.g., event listeners), it can cause memory leaks.
Learn more →Remove event listeners when components are destroyed using removeEventListener. In React, this is handled by useEffect cleanup. Alternatively, use AbortController or once: true option.
Learn more →A design pattern using an IIFE (Immediately Invoked Function Expression) that returns an object with public methods. Private variables and functions are hidden in the closure. Pattern: const Module = (function() { var private = 1; return { public: function() {} }; })();
Learn more →Module Pattern provides true privacy via closures - private variables cannot be accessed from outside. ES6 classes have private fields (#private) which are newer, or rely on conventions (_private) in older code.
Learn more →__proto__ is the actual object that is used in the lookup chain (on instances). prototype is a property of constructor functions that is used as the __proto__ for instances created with new.
Learn more →No. Setting a property always creates or updates an own property on the object itself. It never modifies the prototype. This is called shadowing when the property name exists on the prototype.
Learn more →instanceof checks if the constructor's prototype property appears anywhere in the object's prototype chain. It walks up __proto__ links looking for a match.
Learn more →Mostly yes. Classes desugar to constructor functions with prototype methods. However, classes have some differences: they must be called with new, methods are non-enumerable, and class declarations are not hoisted.
Learn more →A constructor function. The constructor() method becomes the function body. Instance methods become properties on Constructor.prototype. Static methods become properties on the constructor function itself.
Learn more →setTimeout specifies a minimum delay, not an exact time. The callback only runs when the call stack is empty and after the minimum delay has passed. If the main thread is busy, the callback waits longer.
Learn more →Callbacks from Web APIs like setTimeout, setInterval, I/O operations, and UI events. They queue in the Task Queue and the event loop runs one per iteration.
Learn more →Microtasks (Promises, queueMicrotask) have higher priority. The event loop drains ALL microtasks before running the next macrotask. Macrotasks (setTimeout, I/O) run one per iteration.
Learn more →3, 2, 1. Sync code (3) runs first. Promise microtask (2) runs next. setTimeout macrotask (1) runs last.
Learn more →1) Execute one macrotask from the task queue. 2) Drain the entire microtask queue (run all microtasks, including any queued during step 1). 3) Render UI updates if needed. 4) Repeat.
Learn more →When one type of task (microtasks or long sync code) prevents other tasks from running. Example: infinite Promise chain prevents setTimeout callbacks from executing.
Learn more →1) Break work into chunks with setTimeout yielding. 2) Use Web Workers for CPU-intensive tasks. 3) Use requestIdleCallback for non-urgent work. 4) Avoid infinite microtask loops.
Learn more →A higher-order function is a function that accepts a callback as a parameter, or returns a function, or both. Examples: map, filter, reduce.
Learn more →Use Promises with .then() chaining, use async/await, break callbacks into named functions, or use modularization. Modern JavaScript prefers async/await.
Learn more →Extract into named functions or convert to Promises/async-await for a flatter structure.
Learn more →A Node.js convention where callbacks receive (error, result). If an error occurred, error contains the Error object; otherwise, it is null. The second argument contains the successful result.
Learn more →It forces developers to check for errors. If it were the second parameter, it would be easy to forget to check it. Being first makes it visually prominent.
Learn more →The executor function runs synchronously and immediately when the Promise is created. Only the resolve/reject calls may be scheduled asynchronously.
Learn more →No. Once a Promise is settled (fulfilled or rejected), it cannot change state. Additional calls to resolve or reject are ignored.
Learn more →.catch() catches errors from the entire chain above it. The second argument to .then() only catches errors from the immediate previous Promise. .catch() is generally preferred.
Learn more →Throwing an error in .then() causes the returned Promise to reject. This rejection is caught by the next .catch() in the chain.
Learn more →.then() always returns a new Promise. If you return a value, the Promise resolves with that value. If you return a Promise, it adopts that Promise's state.
Learn more →The next .then() receives undefined immediately. If you were expecting a Promise result, you must return the Promise from the previous .then().
Learn more →An error (rejection or thrown exception) skips all remaining .then() handlers and propagates to the next .catch() in the chain.
Learn more →Promise.all() rejects immediately when any Promise rejects (fail-fast). Promise.allSettled() waits for all Promises to settle and never rejects, returning the status of each.
Learn more →Use Promise.race() between the fetch Promise and a timeout Promise that rejects after a delay.
Learn more →Promise.any() returns the first Promise to fulfill, ignoring rejections. If all Promises reject, it rejects with an AggregateError.
Learn more →It is syntactic sugar over Promises. The async keyword makes a function return a Promise. The await keyword pauses execution until a Promise settles, then returns its value.
Learn more →No. await only pauses the async function, not the main thread. Other JavaScript continues running while waiting for the Promise.
Learn more →try/catch handles errors from multiple await calls in one place. .catch() on a specific Promise handles only that Promise's rejection. Use try/catch for grouped operations, .catch() for specific fallbacks.
Learn more →This runs fetches sequentially, one after another. For independent operations, use ids.map(id => fetch(id)) then Promise.all() to run them in parallel.
Learn more →When operations depend on each other (e.g., need user ID before fetching posts). Also when rate limiting or resource constraints apply.
Learn more →Exponential backoff doubles the delay between retries (e.g., 1s, 2s, 4s, 8s). Jitter adds a random offset to each delay so that many clients retrying simultaneously do not all hit the server at the same instant, which would cause a thundering-herd problem.
Learn more →When the dataset is large or unbounded — paginated APIs, streaming logs, real-time events. Async generators yield items lazily, so memory stays constant and the consumer can break early without fetching unnecessary data.
Learn more →Operational errors are expected runtime conditions like network failures, timeouts, or invalid user input — they should be handled gracefully with retries or fallbacks. Programmer errors are bugs like TypeErrors or null dereferences — they indicate code that needs to be fixed, not caught and suppressed.
Learn more →In the basic Module Pattern, public methods may be defined inline in the return object. In the Revealing Module Pattern, all methods are defined as private functions first, then explicitly mapped in the return object, making the public API easier to read and modify.
Learn more →When you need runtime encapsulation within a single file (ES modules provide file-level scope but not sub-file privacy), when building libraries that must expose a controlled API, or when working in environments that do not support ES module syntax.
Learn more →Singletons introduce hidden global state, making code harder to test and reason about. Tests can leak state between each other, and components become tightly coupled to the singleton instead of receiving dependencies explicitly.
Learn more →A factory function is a regular function that returns a new object — no new keyword, no prototype chain, no this binding. A constructor is called with new, sets up this, and implicitly returns the new object with a prototype link.
Learn more →Return an unsubscribe function from the subscribe call and invoke it during cleanup (e.g., in React useEffect return, or component unmount). Use WeakRef or WeakMap for observers when possible, and always remove listeners when the consumer is destroyed.
Learn more →Array.sort accepts a comparator function — that comparator IS the strategy. Array.filter, Array.map, and Promise.then all accept strategy callbacks that define the behavior while the host handles the iteration or async flow.
Learn more →Inheritance adds behavior at the class level and is static — you cannot add or remove it at runtime. Decorators add behavior to individual instances or functions dynamically, and multiple decorators can be composed without creating a deep class hierarchy.
Learn more →The Decorator adds new behavior while preserving the original interface. The Proxy controls access to the original — it may add behavior, but its primary purpose is interception (validation, lazy loading, access control). Decorators enhance, proxies mediate.
Learn more →Reflect methods provide the default behavior for each operation (Reflect.get, Reflect.set, etc.). Without Reflect, you would need to manually implement the default semantics, which is error-prone especially for edge cases like inherited properties and property descriptors.
Learn more →Observer is one-to-many: a single subject notifies its observers directly. Mediator is many-to-many: multiple objects communicate through a central hub. In Observer, the subject knows its observers. In Mediator/Pub-Sub, publishers and subscribers are completely decoupled.
Learn more →The Redux store acts as a mediator. Components dispatch actions (publish) and subscribe to state changes (subscribe). Components never communicate directly — all state flows through the store, which is a centralized Pub/Sub bus with a reducer controlling how events modify state.
Learn more →This difference proves let/const ARE hoisted! The variable exists (so typeof knows about it) but is in TDZ. Undeclared variables truly don't exist, so typeof safely returns "undefined".
Learn more →ReferenceError! The right-hand side x is evaluated while x is still in TDZ. A variable cannot reference itself during initialization.
Learn more →Lexical scope is static (determined at write time). "this" is dynamic (determined at call time). Regular functions get dynamic this, arrow functions use lexical this.
Learn more →The ECMAScript spec requires O(n log n) time complexity. V8 (Chrome/Node) uses Timsort, which is O(n log n) but optimized for real-world data.
Learn more →let is block-scoped. Each iteration of the loop creates a new binding (new variable) for i. Each closure captures its own i from that iteration's block scope.
Learn more →Partial application fixes some arguments of a function, returning a function with fewer parameters. Currying transforms a multi-argument function into a chain of single-argument functions. Both use closures to remember arguments.
Learn more →function curry(fn) { return function curried(...args) { if (args.length >= fn.length) return fn(...args); return (...next) => curried(...args, ...next); }; }
Learn more →Each iframe has its own global execution context with its own constructor functions (Array, Object, etc.). instanceof checks identity of prototype objects, and prototypes from different realms are different objects.
Learn more →1) Create subclass constructor that calls parent with Parent.call(this, args). 2) Set Subclass.prototype = Object.create(Parent.prototype). 3) Fix Subclass.prototype.constructor = Subclass.
Learn more →Object.create(Parent.prototype) creates an object with the correct prototype without calling the Parent constructor. new Parent() would call the constructor, which might have side effects or require arguments we do not have.
Learn more →A security vulnerability where an attacker manipulates __proto__ to inject properties onto Object.prototype (or other prototypes). Since all objects inherit from Object.prototype, this affects all objects in the application.
Learn more →1) Validate/skip dangerous keys (__proto__, constructor, prototype) in merge functions. 2) Use Object.create(null) for maps (no prototype). 3) Freeze Object.prototype. 4) Use structured cloning instead of JSON.parse for untrusted data.
Learn more →Use a concurrent task pool: create N worker functions that pull from a shared queue. Each worker awaits its task, then grabs the next one. Promise.all on the workers ensures all items are processed with at most N in-flight at once.
Learn more →A circuit breaker tracks consecutive failures to a service. When failures exceed a threshold, it moves to OPEN state and blocks requests for a cooldown period. After the cooldown, it enters HALF_OPEN state and allows one test request. If that succeeds, it returns to CLOSED (normal). If it fails, it goes back to OPEN.
Learn more →Add a reset() method that clears the internal instance, allowing each test to start fresh. Better yet, use dependency injection: pass the shared resource as a parameter rather than importing a singleton directly, so tests can substitute a mock.
Learn more →When you need to create families of related objects that must be used together consistently — for example, a UI theme system where buttons, inputs, and cards all need to match. The abstract factory ensures all pieces come from the same family.
Learn more →In the Observer Pattern, the subject directly notifies its observers — they have a direct reference. In Pub/Sub, publishers and subscribers communicate through a mediator (event bus) and have no direct knowledge of each other, providing even looser coupling.
Learn more →A factory can select and return the appropriate strategy based on a type parameter. For example, a getValidator(type) factory returns the correct validation strategy function, decoupling both the creation and the usage of strategies.
Learn more →With pipe (left-to-right), the leftmost decorator wraps first and executes last (outermost layer). With compose (right-to-left), the rightmost wraps first. Decorators execute from outside in: the outermost wrapper runs its pre-logic first, then delegates inward.
Learn more →Vue 3 wraps reactive objects with a Proxy. The get trap tracks which properties are read during component rendering (dependency collection). The set trap triggers re-renders when tracked properties change. This replaced Vue 2's Object.defineProperty approach, enabling reactive arrays and new property detection.
Learn more →When too much logic is placed in the mediator itself (validation, transformation, side effects), it becomes a monolithic class that is hard to test and modify. The mediator should only route messages — business logic belongs in the participants or in dedicated service functions.
Learn more →