Skip to content

Interview questions

Interview questions filterable by role, topic and level: over a hundred deep dives with full answers, plus questions drawn from blog posts that link to the exact section.

304 questions

  1. IntermediateJavaScript12 min read

    What are async/await and how do they work?

    Suggested answer

    Async/await is syntactic sugar built on top of Promises that allows asynchronous code to be written in a synchronous-looking manner. An async function always returns a Promise, and the await keyword pauses execution of the current async function until a Promise settles. Await does not block the JavaScript thread; it simply yields control back to the Event Loop. Under the hood, async/await is transformed into Promise chains. For independent asynchronous operations, Promise.all should be used to execute tasks concurrently and improve performance.

    Read the full answer
  2. StarterJavaScript6 min read

    What is the difference between call, apply, and bind in JavaScript? When would you use each, and how do you implement a custom polyfill for Function.prototype.bind?

    Suggested answer

    call, apply, and bind are utility methods used to explicitly bind a function\'s execution context (this). Both call and apply invoke functions immediately; the syntactic difference lies in their signature, where call accepts arguments as a comma-separated sequence and apply accepts arguments wrapped in a single array. bind differs by returning a new closure where the context and initial arguments are locked, allowing deferred execution. A custom implementation of bind leverages closures to hold references to the target function and initial arguments, returning a nested wrapper that executes the target function using Function.prototype.apply to merge initial binding parameters with active invocation parameters.

    Read the full answer
  3. AdvancedJavaScript14 min read

    What is a closure in JavaScript?

    Suggested answer

    A closure is formed when a function retains access to variables from its lexical scope even after the outer function has completed execution. JavaScript functions capture references to their surrounding environment at creation time, allowing those variables to remain accessible later. Closures power many important JavaScript patterns including data encapsulation, event handlers, debouncing, throttling, memoization, and React Hooks. While closures are extremely useful, they can also increase memory usage because referenced variables cannot be garbage collected until all closures referencing them are removed.

    Read the full answer
  4. IntermediateJavaScript7 min read

    What is currying, and how does it differ from partial application? How do closures support these patterns, and how would you implement a function to handle infinite currying?

    Suggested answer

    Currying is a functional transformation that splits a multi-argument function of arity $N$ into a series of unary functions that resolve on the $N$-th invocation. Partial application fixes a subset of arguments ahead of execution, yielding a function that accepts the remaining parameters. Both patterns rely on JavaScript closures, which retain access to variables in their lexical environment after outer functions return. A custom curry wrapper evaluates the accumulated arguments length against the expected target function length. If the argument quota is met, it executes the target function; otherwise, it returns a closure that merges existing arguments with incoming ones via recursion.

    Read the full answer
  5. IntermediateJavaScript12 min read

    What is the difference between Debounce and Throttle?

    Suggested answer

    Debounce and throttle are performance optimization techniques used to control the frequency of function execution. Debounce delays execution until a period of inactivity has passed, making it ideal for search inputs, form validation, and auto-save features. Throttle limits execution to at most once per specified interval, making it ideal for scroll, resize, and mouse movement events. Internally, both techniques rely on closures to maintain state between invocations. The choice depends on whether the application needs the final event only (debounce) or regular updates during continuous activity (throttle).

    Read the full answer
  6. StarterJavaScript6 min read

    What are event bubbling, event capturing, and event delegation in JavaScript? How do they work, and why is event delegation beneficial for performance?

    Suggested answer

    DOM event propagation operates in three phases: capturing, target, and bubbling. In event delegation, we exploit the bubbling phase by mounting a single event listener on a common parent ancestor rather than mounting hundreds of individual listeners on list items. This drastically reduces runtime memory usage and page-load CPU cycles. When an event fires, it bubbles up to the ancestor, where we compare event.target (the interaction source node) with a match condition using selector functions like .closest(). This pattern also naturally accommodates dynamic DOM mutations, as new nodes appended under the parent container immediately trigger the parent delegation listener without requiring manual event registration or teardown bindings.

    Read the full answer
  7. AdvancedJavaScript16 min read

    How does the JavaScript Event Loop work?

    Suggested answer

    JavaScript is single-threaded and executes code using a Call Stack. Asynchronous operations such as timers, network requests, and DOM events are handled by browser Web APIs. Once completed, their callbacks are placed into task queues. The Event Loop continuously checks whether the Call Stack is empty and then moves queued tasks onto the stack for execution. Promise callbacks and async/await continuations are scheduled as Microtasks, while setTimeout callbacks are scheduled as Macrotasks. The Event Loop always processes all Microtasks before executing the next Macrotask, which is why Promise callbacks execute before timers.

    Read the full answer
  8. AdvancedJavaScript12 min read

    The Call Stack, Memory Heap, and Execution Context in JavaScript

    Suggested answer
    • The Execution Context is the environment where JavaScript code is evaluated.
    • The Call Stack uses LIFO order to manage function calls and store primitive values.
    • The Memory Heap allocates unstructured memory for dynamically-sized objects, arrays, and closures.
    • The stack stores *references* (pointers) to objects that reside in the heap.
    • Modern engines use Mark-and-Sweep garbage collection, tracing memory from global and stack roots to clean unreachable allocations.
    • Keep functions shallow and clear references to unused large data sets to avoid stack overflows and memory heap leaks.
    Read the full answer
  9. AdvancedJavaScript8 min read

    What are iterators and generator functions in JavaScript? How does the iterator protocol work, and how do you write an async generator to poll or stream data chunk-by-chunk?

    Suggested answer

    The Iterator protocol requires a Symbol.iterator method that returns an object containing a .next() signature, which outputs { value, done } objects. Generator functions (function*) act as factory routines for Iterators. They suspend their execution contexts and call stacks upon hitting a yield statement, resuming state parameters only when .next() is called. In asynchronous environments, Async Generators (async function*) throw or yield Promises at each suspend boundary, allowing developers to consume chunked or paginated network streams natively using for await...of loops. This provides a highly clean, sequential way to handle asynchronous streams without managing raw callbacks or streams API complexities.

    Read the full answer
  10. IntermediateJavaScript12 min read

    What is hoisting in JavaScript?

    Suggested answer

    Hoisting is JavaScript's behavior of creating bindings for variables and functions during the creation phase of an execution context before code execution begins. Variable declarations using var are hoisted and initialized with undefined, while let and const are hoisted but remain uninitialized inside the Temporal Dead Zone until their declaration is evaluated. Function declarations are fully hoisted, allowing them to be invoked before their definition appears in the source code. Hoisting is not the physical movement of code but a consequence of how JavaScript builds execution contexts.

    Read the full answer
  11. IntermediateJavaScript10 min read

    JavaScript Scopes: Global, Function, and Lexical Scope

    Suggested answer
    • Scope defines variable visibility rules.
    • Var is function-scoped; Let/Const are block-scoped (restricting access to curly braces {}).
    • JavaScript is Lexically Scoped—scoping is determined by where functions are written in the source code, not where they are executed.
    • The Scope Chain is the path resolved by the engine looking up nested parent lexical environments.
    • Shadowing occurs when a local variable hides an outer variable of the same name.
    • Always use 'use strict' or ESM to prevent accidental global scope leakages.
    Read the full answer
  12. StarterJavaScript8 min read

    What is the difference between map, filter, and reduce in JavaScript? When should you use each?

    Suggested answer

    In JavaScript, map, filter, and reduce are functional, non-mutating array methods used for traversing and transforming collections. map creates a new array of the same length by applying a callback to each element. filter creates a subset array containing only elements that return truthy from a predicate callback. reduce iterates over the array to accumulate its values into a single return value—which can be a number, string, array, or object—based on an accumulator parameter and an optional initial value. Because map and filter return arrays, they support declarative chaining, allowing developers to avoid imperative for loops and state mutations.

    Read the full answer
  13. AdvancedJavaScript14 min read

    What is a memory leak in JavaScript?

    Suggested answer

    A memory leak occurs when memory that is no longer needed remains reachable and therefore cannot be reclaimed by JavaScript's garbage collector. Since JavaScript uses automatic memory management, developers do not manually free memory, but they are responsible for removing unnecessary references. Common sources of leaks include global variables, closures retaining large objects, event listeners, timers, detached DOM nodes, unbounded caches, and improperly cleaned React effects. Garbage collection can only remove unreachable objects, so memory leaks occur when references remain alive longer than intended.

    Read the full answer
  14. StarterJavaScript6 min read

    What is the difference between Primitive and Reference types in JavaScript? How are they stored in memory?

    Suggested answer

    JavaScript separates data into primitive types and reference types based on storage mechanics. Primitive types are stored directly in stack memory by value, meaning copy actions duplicate the actual value. Reference types, such as objects and arrays, reside in heap memory, and their variables in the stack store pointers pointing to their heap address. Consequently, assigning an object to another variable simply copies the reference, meaning mutations propagate to all variables pointing to that heap location. To replicate objects independently, developers must choose between a shallow copy (using spreads or Object.assign) or a deep copy (using structuredClone) depending on the complexity of nested properties.

    Read the full answer
  15. AdvancedJavaScript14 min read

    Programming Paradigms in JavaScript: Functional vs. Object-Oriented

    Suggested answer
    • Multi-Paradigm: JavaScript supports both FP and OOP, allowing you to choose the best pattern for the specific task.
    • OOP encapsulates state and methods inside mutable objects, inheriting behavior via prototype delegation.
    • FP relies on pure functions, data immutability, and function composition to create highly testable and predictable pipelines.
    • Prefer Composition over Inheritance to avoid rigid class models and fragile structures.
    • Modern React patterns (like Hooks and functional components) lean heavily Functional, whereas utility libraries and backend Node systems frequently combine both styles.
    Read the full answer
  16. AdvancedJavaScript15 min read

    What is a Promise and why do we need it?

    Suggested answer

    A Promise is an object that represents the eventual completion or failure of an asynchronous operation. It can be in one of three states: pending, fulfilled, or rejected. Promises solve callback hell by providing a structured way to compose asynchronous operations through chaining. Methods such as then, catch, and finally allow success handling, error handling, and cleanup. Promise callbacks are scheduled in the Microtask Queue, which gives them higher priority than timer callbacks in the Event Loop. Modern async/await syntax is built on top of Promises and provides a cleaner way to write asynchronous code.

    Read the full answer
  17. AdvancedJavaScript14 min read

    How does inheritance work in JavaScript?

    Suggested answer

    JavaScript uses prototype-based inheritance rather than classical inheritance. Every object has an internal reference to another object known as its prototype. When a property or method is accessed, JavaScript first searches the object itself and then walks up the prototype chain until it finds the property or reaches null. Constructor functions and ES6 classes both rely on this mechanism internally. Methods placed on a constructor's prototype are shared across all instances, making prototype inheritance memory efficient and fundamental to JavaScript's object-oriented model.

    Read the full answer
  18. StarterJavaScript6 min read

    What is the difference between double equals (==) and triple equals (===) in JavaScript?

    Suggested answer

    Double equals (==) compares values after performing implicit type coercion, trying to convert both operands to a matching primitive type (usually numeric). Triple equals (===) compares both the value and the type strictly, returning false if types do not align. For example, 5 == "5" evaluates to true because the string is coerced to a number, whereas 5 === "5" is false due to type mismatch. Because loose coercion rules lead to unintuitive behaviors (like [] == false evaluating to true), strict equality (===) is the industry standard for code predictability, with double equals reserved almost exclusively for checking if a value is null or undefined simultaneously (val == null).

    Read the full answer
  19. IntermediateJavaScript12 min read

    What is this in JavaScript and how does it work?

    Suggested answer

    The value of this in JavaScript is determined by how a function is invoked. In global scope, this usually refers to the global object. In object methods, it refers to the object before the dot. Constructor functions bind this to newly created instances via new. Functions can explicitly set this using call, apply, and bind. Arrow functions are different because they do not have their own this; instead they inherit it lexically from the surrounding scope. Understanding invocation patterns rather than memorizing rules is the key to mastering this.

    Read the full answer
  20. IntermediateJavaScript7 min read

    What are WeakMap and WeakSet in JavaScript? How do they differ from Map and Set, and what specific problem do they solve regarding garbage collection?

    Suggested answer

    The fundamental difference between Map and WeakMap is the reference strength of keys. A Map establishes strong references, preventing garbage collection of key objects. A WeakMap holds weak references to its keys, meaning if a key object lacks other active references in the execution stack, it is garbage collected, and its entry is cleared from the WeakMap automatically. To make this garbage collection lookup deterministic, WeakMap keys must be object types rather than primitive values, and the collection is non-iterable, preventing exposure of intermediate garbage collection states. Major use cases include metadata association for DOM nodes, private scoping structures, and non-blocking caching layers.

    Read the full answer
  21. StarterTypeScript6 min read

    What is the difference between any and unknown in TypeScript? When should you choose one over the other?

    Suggested answer
    • unknown Type: Use unknown by default when you don't know the incoming type (e.g., fetch request responses, dynamic input fields) to keep the codebase type-safe.
    • any Type: Avoid any unless migrating legacy JavaScript or writing complex generic libraries where type safety is managed via other assertions.
    • Top Type Behavior: Both any and unknown are top types (all values are assignable to them), but unknown cannot be assigned to anything else without narrowing.
    Read the full answer
  22. IntermediateTypeScript7 min read

    What are declaration files (.d.ts) in TypeScript, and how does declaration merging work? Give a concrete example of how to extend a third-party library's interface (e.g. Express Request object) using module augmentation.

    Suggested answer

    Declaration files (.d.ts) act as compile-time metadata describing JavaScript interfaces to the compiler without emitting runtime code. Declaration merging occurs when the compiler combines identical-named interfaces, namespaces, or classes into a unified construct. When extending third-party module interfaces (such as adding authenticated user fields to Express request items), standard global scope interfaces fail because of module isolation. We resolve this by declaring module augmentation using declare module 'module-name' or declare global blocks. Inside these blocks, we redeclare target interfaces, triggering interface merging, which expands the compiler\'s type registry for those library modules.

    Read the full answer
  23. AdvancedTypeScript8 min read

    What are Mapped Types and Template Literal Types in TypeScript? How do you use key remapping (the as clause) to dynamically transform object key names or filter keys during compilation?

    Suggested answer

    Mapped types iterate over a set of keys to dynamically generate object structures. Template literal types build upon string union types, allowing string concatenations and modifications to resolve at compile-time. We can combine these features using key remapping via the as clause to rename keys dynamically (e.g. capitalizing properties to output getter signatures) or filter out keys. By resolving key definitions to never inside a conditional type check during the as remapping cycle, TypeScript excludes those properties entirely, providing a declarative mechanism to implement custom utility transformations.

    Read the full answer
  24. StarterTypeScript6 min read

    How do you declare immutable properties in TypeScript? What is the difference between readonly, the Readonly<T> utility type, and as const assertions?

    Suggested answer
    • readonly Modifier: Prevents properties on an interface or class from being reassigned after initialization.
    • Readonly Utility: Wraps an entire object type to make all its properties read-only without manual keys.
    • as const Assertions: Locks down literal values recursively to read-only, preventing type widening to primitive types.
    • ReadonlyArray: Prevents mutating array methods like push and pop from running on an array at compile-time.
    Read the full answer
  25. IntermediateTypeScript8 min read

    What is Type Narrowing in TypeScript, and how do you write a custom (User-Defined) Type Guard using the is keyword?

    Suggested answer
    • Type Predicates: Return parameterName is Type from a function to tell the compiler that returning true guarantees the variable type.
    • Narrowing Operators: Leverage typeof, instanceof, and in for standard Javascript object structures.
    • discriminated unions: Use literal fields to create robust control flows that scale without writing manual checks.
    • exhaustive checking: Use the never type in your switch fallbacks to guarantee all union states are handled.
    Read the full answer
  26. AdvancedTypeScript6 min read

    TypeScript Generics and Built-in Utility Types

    Suggested answer
    • Generics: Write code that remains flexible while maintaining strict compile-time types.
    • Utility Types: Standard utilities like Pick and Omit use mapped types under the hood to transform object shapes.
    • infer Keyword: Allows you to extract type parameters out of functions, arrays, and promises dynamically.
    Read the full answer
  27. AdvancedTypeScript6 min read

    TypeScript Conditional Types and the infer Keyword

    Suggested answer
    • Conditional types allow writing if/else checks within the type system using T extends U ? X : Y.
    • The infer keyword is a powerful pattern-matching tool that declares a generic variable on-the-fly inside a condition.
    • infer can only be placed inside the extends clause of a conditional type.
    • Common utilities like ReturnType, Parameters, and Awaited are built entirely on top of the infer keyword.
    Read the full answer