277 JavaScript Frontend Developer Interview Questions & Answers

133 top • 30 Amazon • 15 Apple • 29 Google • 18 Meta • 21 Microsoft • 20 Netflix • 11 NVIDIA

JavaScript Frontend Developer icon

Questions with Detailed ExplanationsWith Detailed Explanations

(Last Updated: August 28, 2026)

21. What is the JavaScript event loop?Language SpecificEasy

Question Details

Define the browser event loop as the coordination mechanism that lets tasks run, microtasks drain, and rendering opportunities occur while JavaScript execution remains run-to-completion on an agent. Explain the call stack, task queue, promise microtasks, timers, user events, long-running work, and why asynchronous APIs do not make ordinary JavaScript statements run in parallel.

Short Interview Answer (30-60 seconds)

The JavaScript event loop coordinates when queued work can run after the current JavaScript work finishes. Code on the call stack runs to completion first. After a task finishes, Promise microtasks are drained before the browser takes another task, with rendering opportunities occurring when the browser is ready to render. Timers and user events schedule future tasks, but they do not make ordinary JavaScript statements run in parallel. Long running JavaScript can still block the page.

Detailed Explanation

The event loop is the browser's way of deciding when different pieces of work get a turn. The work happening now must finish before another piece can start. Other browser features can wait for a timer, a click, or a network result without stopping everything. When waiting work becomes ready, its related action is placed in line for later. This matters because one slow piece of work can delay clicks, screen updates, and other ready work, making a page feel slow or frozen.

Useful Questions to Ask the Interviewer
  1. Should I focus on how the event loop works in a web browser?
  2. Would you like me to compare Promise callbacks with timer callbacks?
What is the JavaScript event loop? diagram
How to Explain It in an Interview

The practical rule is that JavaScript finishes the current task before another task starts on the same agent. The call stack shows the JavaScript functions that are currently executing. A task can come from sources such as initial script execution, a timer becoming ready, or a user event such as a click.

Browser Web APIs can wait for outside events while the current JavaScript continues. For example, a timer can count down without interrupting code that is already running. When the timer becomes eligible, its callback can be queued as a future task. A zero delay timer is therefore not an instruction to run immediately.

Promises use microtasks. When a Promise reaction becomes ready, its callback is queued as a microtask. After the current task finishes and the call stack is empty, the browser performs a microtask checkpoint. It keeps processing queued microtasks until the queue is empty, including new microtasks added while that checkpoint is running.

After this work, the browser may have an opportunity to render before taking later tasks. Rendering is controlled by browser scheduling, so it is not guaranteed after every task. The browser cannot paint in the middle of ordinary JavaScript that is still running.

This explains why a long calculation can freeze input and visual updates. Async APIs improve coordination, but async and await do not move CPU work to another thread. For expensive computation, a Web Worker can run JavaScript in a separate execution context. In production, keep main thread work short and avoid creating an unbounded chain of microtasks.

Where it is used

The event loop matters whenever frontend code uses timers, DOM events, Promises, async functions, network requests, animation, or other browser APIs. Developers use this knowledge to predict callback order, keep the user interface responsive, understand why a zero delay timer still waits, avoid excessive microtask chains, and decide when expensive CPU work should move to a Web Worker.

Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands how browser JavaScript coordinates current work with delayed work, user actions, Promise callbacks, and rendering. They also want to see whether the candidate understands that ordinary JavaScript code runs to completion on its current execution thread. This knowledge helps a frontend developer reason about callback order, blocked pages, incorrect timing assumptions, and responsive user interfaces.

Common interview mistakes

A common mistake is saying that asynchronous JavaScript makes ordinary main thread statements run in parallel. Another mistake is saying that a zero delay timer runs immediately. Its callback still has to wait until it is eligible and the current task has finished. Candidates also sometimes treat Promise callbacks and timer callbacks as the same kind of queued work. Promise reactions use microtasks, which are drained at a microtask checkpoint before another task is selected. Another mistake is assuming the browser can render while a long JavaScript function is still executing.

Interview tip

Start with the execution order. Say that the current task finishes first, then Promise microtasks are drained, and later tasks such as timer callbacks or user events can run. Mention that rendering happens only when the browser gets an opportunity to render. Finish by explaining that asynchronous APIs do not make ordinary main thread JavaScript run in parallel and that long tasks can block the page.

Interviewer may ask next
What happens if a Promise callback keeps adding new Promise microtasks?

The browser keeps processing those microtasks during the same microtask checkpoint until the microtask queue becomes empty. New microtasks added while the checkpoint is running can therefore keep extending that checkpoint. This matters because a very long or unbounded microtask chain can delay later tasks and delay a rendering opportunity. The tradeoff is that microtasks provide very prompt follow up work, but excessive use can hurt responsiveness.

What should you do if a calculation takes a long time and blocks the event loop?

Move suitable CPU heavy work to a Web Worker, or divide the calculation into smaller pieces that regularly return control to the browser. A Web Worker runs JavaScript in a separate execution context, so the main thread can remain available for input and rendering. This matters for responsive interfaces. The main tradeoff is added communication cost and more complex data exchange between the main thread and the worker.

22. What is a JavaScript promise?Language SpecificEasy

Question Details

Define a Promise as an object representing the eventual completion or failure of an asynchronous operation. Explain pending, fulfilled, and rejected states; then, catch, and finally; value and error propagation; chaining; promise microtasks; and how async and await consume promises. Clarify that creating a Promise does not automatically move CPU work to another thread.

Short Interview Answer (30-60 seconds)

A JavaScript Promise is an object that represents a result that may become available later. It starts pending and then becomes either fulfilled with a value or rejected with a reason. I use then for successful results, catch for errors, and finally for cleanup that should run either way. Promise reactions run as microtasks after the current synchronous work finishes. Async and await provide cleaner syntax for consuming Promises. Creating a Promise does not move CPU work to another thread.

Detailed Explanation

See the Code while reading this explanation.

A Promise is a JavaScript object used when a result may arrive later. For example, a page may request user data from a server. The request does not finish immediately, so the Promise represents the future result. It can still be waiting, finish successfully, or fail. Code can react when the result becomes ready, handle a failure, and run cleanup afterward. This helps developers organize work that happens over time without blocking the normal flow of the page.

Useful Questions to Ask the Interviewer
  1. Would you like me to explain Promise scheduling with the browser event loop and microtask queue?
  2. Would you like an example showing then, catch, finally, and async and await?
What is a JavaScript promise? diagram
How to Explain It in an Interview

A Promise represents the eventual completion or failure of an asynchronous operation. A new Promise begins in the pending state. It can later become fulfilled with a value or rejected with a reason, often an Error object. Once a Promise is fulfilled or rejected, it is settled and its state cannot change again.

The function passed to the Promise constructor runs synchronously when the Promise is created. Calling resolve or reject settles the Promise according to the Promise resolution rules. Creating a Promise by itself does not make its executor run in the background.

The then method registers reactions for a Promise and returns a new Promise. This makes chaining possible. If a then callback returns a normal value, the Promise returned by then is fulfilled with that value. If it returns another Promise or compatible thenable, the returned Promise adopts that result. If the callback throws, the returned Promise becomes rejected.

The catch method handles rejection and is equivalent to calling then with only a rejection handler. A rejection moves through later chain steps until a rejection handler handles it. The finally method is mainly for cleanup. Its callback receives neither the fulfilled value nor the rejection reason as an argument. If finally completes normally, the original result continues through the chain. If it throws or returns a rejected Promise, that new failure becomes the result.

Promise reactions do not run immediately when they are registered. When a reaction becomes ready, JavaScript schedules it as a microtask. After the current call stack becomes empty, the runtime processes ready microtasks before moving to a later task such as another timer or event. This ordering is important when reasoning about output order and user interface responsiveness.

An async function always returns a Promise. Inside an async function, await consumes a Promise or other value. If the awaited Promise fulfills, await produces its value. If it rejects, await throws that rejection inside the async function, where try and catch can handle it. Await pauses only that async function. It does not block the whole JavaScript runtime.

Promises are useful for asynchronous results such as network requests. They are not threads. Creating or awaiting a Promise does not move CPU intensive JavaScript away from the browser main thread. For suitable CPU intensive browser work, a Web Worker may be used instead.

Example

This example starts an operation that completes later and resolves with a user name. The first then receives that value and transforms it. The next then receives the transformed value. Catch handles a rejection or thrown error from earlier in the chain. Finally runs after fulfillment or rejection for cleanup. The async function shows another way to consume the same Promise with await and try and catch. Both forms use the same Promise behavior. Neither form creates another thread.

Code
function loadUserName() {
  return new Promise((resolve) => {
    // The timer represents work whose result becomes available later.
    setTimeout(() => {
      // Resolving settles this Promise successfully with one value.
      resolve('Maya');
    }, 100);
  });
}

loadUserName()
  .then((name) => {
    // Returning a normal value fulfills the next Promise in the chain.
    return name.toUpperCase();
  })
  .then((name) => {
    // This reaction receives the value produced by the previous step.
    console.log('Promise chain:', name);
  })
  .catch((error) => {
    // A rejection or thrown error from an earlier step reaches this handler.
    console.error('Promise error:', error);
  })
  .finally(() => {
    // Cleanup runs after either fulfillment or rejection.
    console.log('Promise finished');
  });

async function showUserName() {
  try {
    // Await consumes the Promise and pauses only this async function.
    const name = await loadUserName();
    console.log('Async function:', name.toUpperCase());
  } catch (error) {
    // A rejected awaited Promise is handled like a thrown error here.
    console.error('Async error:', error);
  }
}

showUserName();
Where it is used

Promises are common in frontend code that waits for asynchronous results. Fetch returns a Promise, so applications use Promises when loading API data. Promises are also used by many browser and application APIs that expose results which complete later. In production code, they help coordinate loading states, dependent asynchronous steps, error handling, and cleanup. Async and await are often used because they make Promise based control flow easier to read. When an operation supports cancellation, such as fetch, AbortController can request cancellation of that operation. A Promise itself does not provide general cancellation.

Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands asynchronous JavaScript, Promise states, value and error propagation, chaining, microtask scheduling, and how async and await consume Promises. They also want to see whether the candidate knows that a Promise represents a future result but does not create another thread or move heavy CPU work away from the main JavaScript execution context.

Common interview mistakes

A common mistake is thinking that a Promise starts another thread. It does not. The Promise constructor executor runs synchronously when the Promise is created, although asynchronous operations started inside it may finish later. Another mistake is forgetting to return a Promise or value from a then callback when the next step depends on that result. Developers may also forget to handle rejection, which can lead to unhandled Promise rejection reports. Another mistake is assuming finally receives the fulfilled value or rejection reason. Its callback is mainly for cleanup and does not receive those values as normal arguments. Developers should also remember that await pauses only the current async function, not the entire JavaScript runtime.

Interview tip

Start by saying that a Promise represents a future result and has pending, fulfilled, and rejected states. Then explain then, catch, and finally, followed by value and error propagation through chaining. Mention that Promise reactions run as microtasks. Finish by explaining that async and await consume Promises and that Promises do not create threads. This order gives the interviewer the main idea first and then shows that you understand the runtime behavior.

Interviewer may ask next
When does a then callback run if a Promise is already fulfilled?

It still does not run immediately inside the current synchronous code. Calling then on an already fulfilled Promise schedules its reaction as a microtask. That microtask runs after the current call stack becomes empty. This matters because Promise reactions keep predictable asynchronous ordering even when the Promise already has a result. A large amount of work placed into continuously scheduled microtasks can delay later tasks such as timers, events, and rendering opportunities.

Should a Promise be used to move heavy CPU work away from the browser main thread?

No. Creating or awaiting a Promise does not move CPU intensive JavaScript to another thread. The code still runs in its current JavaScript execution context unless another browser mechanism performs work elsewhere. This matters because heavy CPU work on the main thread can delay input handling and rendering. For suitable CPU intensive browser work, a Web Worker can provide a separate execution context. The tradeoff is extra communication, data transfer, and coordination complexity.

23. What is a JavaScript module?Language SpecificEasy

Question Details

Define a JavaScript module as a file with its own module scope that can explicitly export and import bindings. Explain named and default exports, static dependency analysis, strict mode, deferred browser execution, module URLs, live bindings, and dynamic import. Distinguish an ES module from a classic script, a package, and a bundler output chunk.

Short Interview Answer (30-60 seconds)

A JavaScript module is a file with its own module scope that can explicitly export bindings and import bindings from other modules. I use modules to split an application into clear reusable parts. ES modules support named exports, a default export, static imports, live imported bindings, and dynamic import. In browsers, module code always uses strict mode, and normal module scripts are deferred automatically.

Detailed Explanation

A JavaScript module is a way to divide a program into smaller files with clear boundaries. Each file can decide what it shares and what remains private. One file can provide values or functions, and another file can use them. This makes larger applications easier to organize, test, change, and reuse. Modern browsers understand this module system directly. They can discover normal module dependencies before running the code, load the required files, and then evaluate them in dependency order. Modules also avoid placing ordinary top level declarations into the shared global scope.

Useful Questions to Ask the Interviewer
  1. Should I focus on browser ES modules or also compare them with bundler generated files?
  2. Would you like me to explain both static import and dynamic import?
What is a JavaScript module? diagram
How to Explain It in an Interview

An ES module is a JavaScript file that has its own module scope and can use import and export syntax. Ordinary variables declared inside one module do not automatically become global variables.

A named export exposes a binding under a specific name. A module can have several named exports. Another module imports those names explicitly. A module can also provide one default export, which the importing module may give any local name.

Static import declarations describe dependencies before module evaluation. This lets the module system build a dependency graph before running module code. Static import declarations are only valid in module code and appear at the top level of that module.

Imported bindings are read only from the importing module and are live. For example, if module A exports a variable as a binding and later changes that variable, module B sees the current value when it reads its import. Module B cannot directly assign a new value to that imported binding.

ES module code always runs in strict mode. In a browser, a script element whose type is module behaves as deferred by default. It waits until document parsing is complete before normal evaluation. Module dependencies are fetched before the module can run.

Browser module specifiers are resolved using URLs. A relative reference normally needs an explicit form such as ./utils.js. The resolved module URL identifies the module resource used by the browser module loader.

Dynamic import uses import() and returns a Promise that fulfills with a module namespace object after the requested module is loaded and evaluated. It is useful for optional or later needed features.

An ES module is different from a classic script. Classic scripts have different scope, loading, and syntax rules and cannot use static import declarations or export declarations. A module is also different from a package. A package is a distribution unit that can contain many modules and metadata. A bundler output chunk is a generated delivery file. A chunk can contain transformed code from several source modules, so a source module and an output chunk are not the same concept.

Where it is used

ES modules are used throughout modern frontend applications. Teams use them to separate user interface code, data access code, utilities, configuration, and feature logic into focused files. Browsers can load ES modules directly. Build tools can also analyze static import relationships to combine and optimize application code for production. Dynamic import is useful when a large or optional feature should be loaded only after the user needs it.

Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands how modern JavaScript code is divided into files, how those files share bindings, and how browsers load and evaluate module code. They also want to see whether the candidate understands scope, dependency loading, live bindings, dynamic loading, and the difference between language modules, packages, and generated bundler files.

Common interview mistakes

A common mistake is thinking every imported value is an independent copied value. Imports are bindings, and an import connected to an exported binding observes that binding as it changes. The importing module cannot directly reassign the imported binding. Another mistake is treating a module, package, and bundler chunk as the same thing. They are different concepts. Developers may also forget that module code always uses strict mode, that normal browser module scripts are deferred automatically, that browser module references use URL based resolution, and that import() is asynchronous and returns a Promise.

Interview tip

Start by saying that a module is a file with its own scope that explicitly imports and exports bindings. Then explain named and default exports, static dependency discovery, live bindings, strict mode, browser loading behavior, module URLs, and dynamic import. Finish by clearly separating an ES module from a classic script, a package, and a bundler output chunk.

Interviewer may ask next
If an exported variable changes after another module imports it, does the importing module see the new value?

Yes. If the export refers to a binding that later changes, the importing module observes the current value through its live imported binding. This matters because the import is not an independent snapshot of that binding. The importing module can read the updated value, but it cannot directly assign a replacement value to the imported binding.

When would you use dynamic import instead of a static import?

I would use dynamic import when a module should be loaded only when it is needed. import() returns a Promise that fulfills with the module namespace object after loading and evaluation succeed. This is useful for optional features or code that is not needed during initial startup. The tradeoff is that the dependency becomes available asynchronously, loading can fail at runtime, and the application must handle both the waiting state and possible errors.

24. How does lexical scope determine which binding a function reads?Language SpecificEasy

Question Details

Create nested functions with a global label, an outer label, and a block-local label. Ask which binding each function can access based on where it was defined rather than where it was called. Include shadowing and explain the scope-chain lookup from inner to outer environments.

Short Interview Answer (30-60 seconds)

JavaScript decides which binding a function reads from where that function was defined, not from where it is called. It first checks the function's own environment, then moves outward through the environments around its definition. If a nearer environment has the same variable name, that binding shadows the outer one. This makes variable lookup predictable even when the function is called from another scope.

Detailed Explanation

See the Code while reading this explanation.

JavaScript remembers the surroundings where each function was created. Those surroundings decide which names the function can read later. Calling the function from another place does not give it access to the caller's local names. If several surrounding places contain the same name, the closest matching name to the function's definition is used. This matters when functions are inside other functions or blocks. Understanding this rule helps you predict which value a function will print and prevents mistakes when the same variable name appears in several nested places.

Useful Questions to Ask the Interviewer
  1. Should I show the difference between where a function is defined and where it is called?
  2. Should I include a block with another binding using the same variable name?
How does lexical scope determine which binding a function reads? diagram
How to Explain It in an Interview

Lexical scope means JavaScript decides a function's surrounding environments from the place where the function is defined.

Suppose the top level has label with the value global. An outer function creates another label with the value outer. Inside a block, another label has the value block.

A function defined at the top level reads the top level label, even if we call it from inside the outer function or the block. Its lexical environment was fixed when the function was created, so local bindings belonging only to the caller are not added to its lookup path.

A function defined directly inside the outer function reads the outer label. If we call that function from inside the block, it still reads outer. The block is only the call location. It is not one of the lexical environments surrounding that function's definition.

A function defined inside the block reads the block label. That binding shadows the outer and top level bindings because it is the nearest matching binding in the function's scope chain.

For an identifier read, JavaScript starts with the current lexical environment and follows outer lexical environments until it finds a matching binding. If no matching binding exists anywhere in that chain, reading the undeclared identifier throws a ReferenceError.

This behavior also enables closures. A function can keep access to a binding from the environment where it was created after execution has left that environment. In production code, lexical scope is useful and predictable, but excessive shadowing can make code harder to understand.

Example

The example uses a top level label, an outer function binding also named label, and a block binding with the same name. readGlobal is defined at the top level, so it reads global even when called inside the block. readOuter is defined inside outer, outside the nested block, so it reads outer even when called from the block. readBlock is defined inside the block and then stored in escapedBlockReader. When it is called after execution leaves the block, it still reads block because the closure keeps access to the lexical environment where it was created.

Code
const label = 'global';

// This function is created at the top level, so its lexical lookup starts there.
function readGlobal() {
  console.log(label);
}

function outer() {
  const label = 'outer';

  // This function is created inside outer, so the outer binding is the nearest matching name.
  function readOuter() {
    console.log(label);
  }

  let escapedBlockReader;

  {
    const label = 'block';

    // This function is created inside the block, so the block binding shadows the outer binding.
    function readBlock() {
      console.log(label);
    }

    // Calling these functions here does not change the lexical environments chosen when they were created.
    readGlobal();
    readOuter();
    readBlock();

    // Keep the block function so it can be called after execution leaves this block.
    escapedBlockReader = readBlock;
  }

  // The closure still reads the block binding because that required lexical environment remains reachable.
  escapedBlockReader();
}

outer();
Where it is used

Lexical scope is used throughout frontend JavaScript with nested functions, callbacks, event handlers, module functions, and closures. A callback can read configuration or state from the environment where it was created. Block scope with let and const is also common inside conditions and loops. Shadowing is valid JavaScript, but repeatedly using the same variable name in deeply nested scopes can make production code harder to read and maintain.

Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands how JavaScript chooses a variable based on where a function was defined. It also tests whether the candidate understands nested lexical environments, shadowing, closures, and why calling the same function from a different place does not change which surrounding binding it reads.

Common interview mistakes

A common mistake is thinking a function reads local variables from the place where it is called. JavaScript does not use the caller's local lexical environment for that lookup. Another mistake is assuming an outer function can read a variable declared only inside a nested block. It cannot. Candidates also sometimes forget that the nearest binding with the same name shadows bindings farther out. Finally, lexical scope should not be confused with this, because ordinary function calls use different rules to determine this.

Interview tip

Start with the main rule: a function reads surrounding bindings based on where it was defined. Then trace the lookup from the function's own environment outward. Use the three label values to show shadowing, and call the functions from inside the block to prove that the call location does not change lexical scope.

Interviewer may ask next
What happens if none of the lexical environments contains the requested identifier?

Reading an undeclared identifier throws a ReferenceError. JavaScript checks the current lexical environment and then follows each outer lexical environment. If it reaches the outermost environment without finding a matching binding, the lookup fails. This matters because calling the function from a scope that happens to contain the same variable name does not rescue the lookup. The caller's local environment is not inserted into the function's lexical scope chain.

Does keeping the block function after the block finishes have a memory cost?

Yes. The escaped readBlock function keeps access to the block binding that it uses, so the required lexical environment can remain reachable after execution leaves the block. This is closure behavior. It is useful for preserving private state, but a long lived closure can also keep referenced data alive longer than necessary. In production code, the main tradeoff is between convenient retained state and avoiding unnecessary memory retention.

25. When should `===` be preferred over `==`, and what coercion can `==` perform?Language SpecificEasy

Question Details

Compare strict and abstract equality using the pairs 0 and false, '' and 0, null and undefined, and two separately created objects. State which comparisons coerce types, which compare object identity, and why production frontend code normally uses strict equality unless a deliberate coercive rule is required.

Short Interview Answer (30-60 seconds)

I normally prefer === because it compares without converting different types. With ==, JavaScript can perform coercion before comparing. For example, 0 == false and '' == 0 are true. Also, null == undefined is true because of a special equality rule. For two objects, both operators compare identity, so separately created objects are not equal even when their contents look the same. I use == only when I deliberately want its defined coercive behavior.

Detailed Explanation

See the Code while reading this explanation.

In normal frontend code, I would choose the comparison that gives the most predictable result. One form compares values without changing different kinds into matching kinds first. The other form may change one side before checking equality. This matters because values that look unrelated can sometimes be treated as equal. Zero, false, empty text, missing values, and separate objects show the difference clearly. Understanding these examples helps a developer write conditions that behave as expected and helps another developer understand the intention without memorizing surprising conversion rules.

Useful Questions to Ask the Interviewer
  1. Should I explain the exact result for each of the four example pairs?
  2. Should I also mention when deliberate coercive equality can be useful in production code?
When should `===` be preferred over `==`, and what coercion can `==` perform? diagram
How to Explain It in an Interview

Prefer === for normal production code because strict equality does not coerce different types before comparing them. If the operands have different types, strict equality returns false. If they have the same type, JavaScript compares them according to that type's strict equality rules.

The == operator uses abstract equality. Its rules can convert values before comparing them. For example, 0 == false is true because the Boolean value false is converted to the number 0. Also, '' == 0 is true because the empty string is converted to the number 0 for this comparison. More generally, abstract equality can perform conversions such as Boolean to number, string to number in relevant comparisons, and object to primitive when an object is compared with a primitive.

null == undefined is true because abstract equality has a special rule that treats these two values as equal to each other. It is not the result of converting both into one ordinary value. With strict equality, null === undefined is false because they are different types.

Objects use identity when both operands are objects. If I create two separate objects with the same properties, both strict and abstract equality return false because the references point to different objects. If two variables refer to the same object, the comparison returns true.

In production frontend code, I use === by default because its behavior is easier to predict and review. I use == only when I intentionally want a defined coercive rule, such as value == null to match either null or undefined.

Example

The example uses the four pairs from the question. It shows that strict equality does not coerce different primitive types, while abstract equality can apply its defined conversion rules. It also shows the special relationship between null and undefined and demonstrates that separately created objects are unequal because equality compares object identity when both operands are objects.

Code
const firstObject = { value: 1 };
const secondObject = { value: 1 };

// Compare zero and false with both equality rules.
console.log(0 === false); // false
console.log(0 == false); // true

// Abstract equality converts the empty string to zero for this comparison.
console.log('' === 0); // false
console.log('' == 0); // true

// Abstract equality has a special rule for null and undefined.
console.log(null === undefined); // false
console.log(null == undefined); // true

// Separate objects have different identities even when their properties match.
console.log(firstObject === secondObject); // false
console.log(firstObject == secondObject); // false

// The same object reference has the same identity.
console.log(firstObject === firstObject); // true
Where it is used

Strict equality is common in frontend conditions, form validation, state checks, event handling, configuration checks, and API response logic when the expected type is known. Abstract equality can be useful in a deliberate check such as value == null when both null and undefined should mean that a value is missing. In most production code, strict equality makes the intended comparison clearer and reduces accidental coercion.

Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands strict equality, abstract equality, automatic type conversion, and object identity in JavaScript. It also tests whether the candidate can choose predictable comparison rules for production frontend code and recognize the few cases where deliberate coercive equality can be useful.

Common interview mistakes

A common mistake is saying that == simply ignores types. It actually follows defined abstract equality rules and performs specific conversions only when those rules require them. Another mistake is saying that null == undefined is true because both values are converted into the same normal value. Their equality comes from a special rule. Developers also sometimes expect two objects with identical properties to compare as equal. When both operands are objects, equality compares identity, not their property contents. Another mistake is using == without a deliberate reason, which can make conditions harder to understand.

Interview tip

Start by saying that === is the normal production choice because it avoids coercion between different types. Then explain the four requested pairs in order. Clearly separate primitive coercion, the special null and undefined rule, and object identity. Mention value == null only as an intentional production exception.

Interviewer may ask next
Why is null == undefined true while null === undefined is false?

null == undefined is true because abstract equality contains a specific rule that treats these two values as equal to each other. Strict equality does not apply that rule and does not coerce different types, so null === undefined is false because null and undefined have different types. This matters when code intentionally wants one condition that recognizes either form of a missing value.

Is there a reasonable production case for intentionally using == instead of ===?

Yes. A deliberate value == null check can be useful when both null and undefined should be treated as missing. The exact behavior is that this comparison matches either value while values such as 0, false, and an empty string do not match. The tradeoff is readability because coercive equality has more rules to understand, so the intention should be clear to the team.

26. Which JavaScript values are falsy, and how does truthiness affect conditionals?Language SpecificEasy

Question Details

A form handler receives values that may be 0, '', false, null, undefined, NaN, empty arrays, or empty objects. Identify which values fail a Boolean test and which remain truthy. Explain why a generic if (!value) check can incorrectly reject valid numeric or text input.

Short Interview Answer (30-60 seconds)

The standard JavaScript falsy values are false, 0, negative zero, 0n, an empty string, null, undefined, and NaN. They act like false when JavaScript converts them to Boolean values in a condition. Empty arrays and empty objects are truthy. Because of that, I would not use if (!value) to mean missing input when zero, false, or an empty string can be valid. I would check for the exact invalid values instead.

Detailed Explanation

See the Code while reading this explanation.

JavaScript conditions can accept values that are not already true or false. JavaScript first decides whether the value should count as true or false. Some values count as false. Most values count as true. This matters in forms because zero may be real input even though it counts as false in a condition. An empty string also counts as false. Empty arrays and empty objects are different because they count as true. Good validation should therefore describe exactly what is missing or invalid instead of treating every value that counts as false as an error.

Useful Questions to Ask the Interviewer
  1. Can zero be a valid value for this form field?
  2. Can an empty string or false be valid input, or should either be treated as missing?
Which JavaScript values are falsy, and how does truthiness affect conditionals? diagram
How to Explain It in an Interview

When JavaScript evaluates an if condition, it converts the tested value to a Boolean result. The standard falsy values are false, 0, negative zero, 0n, an empty string, null, undefined, and NaN. Each of these makes the condition behave as false.

Most other values are truthy. This includes nonempty strings, nonzero numbers, arrays, functions, and objects. An important edge case is that an empty array and an empty object are both truthy. JavaScript does not inspect their contents when deciding truthiness. The array or object reference itself is an object value, so its Boolean conversion is true.

This is why a generic check such as if (!value) can be unsafe for form validation. If a quantity field accepts 0, the check treats that valid value as false. It can also reject false when false is a valid setting, or an empty string when that string is allowed by the business rule.

If only null and undefined mean missing, I would check value === null || value === undefined. If NaN is invalid, I would test it separately with Number.isNaN(value). If an empty string is invalid, I would check that rule directly.

These checks take constant work and use no meaningful extra memory. They do not copy the input or allocate another collection. The main production concern is correctness and clarity.

In browsers, document.all is a special legacy web compatibility exception that behaves as falsy even though it is object like. It is not a normal rule for JavaScript objects and should not be used as a model for application logic.

Example

The example converts each supplied value with Boolean so the runtime behavior is visible. Zero, an empty string, false, null, undefined, and NaN produce false. The empty array and empty object produce true. The example then uses an explicit missing value check that treats only null and undefined as missing, which preserves valid values such as zero, false, and an empty string.

Code
const values = [0, '', false, null, undefined, NaN, [], {}];

// Show the Boolean result that JavaScript uses when each value is tested in a condition.
for (const value of values) {
  console.log(value, Boolean(value));
}

function isMissing(value) {
  // Only null and undefined represent missing input for this example.
  // This preserves valid falsy values such as 0, false, and an empty string.
  return value === null || value === undefined;
}

console.log(isMissing(0));
console.log(isMissing(''));
console.log(isMissing(false));
console.log(isMissing(null));
console.log(isMissing(undefined));
Where it is used

Truthy and falsy checks are common in form validation, conditional rendering, configuration handling, optional input processing, and API response handling. Explicit checks are especially important when zero, false, or an empty string has a valid business meaning. In those cases, checking only the exact missing or invalid values prevents valid user input from being rejected.

Why Interviewers Ask This

Interviewers ask this to check whether the candidate understands Boolean conversion in JavaScript and can apply it safely in frontend code. They want to see whether the candidate knows the exact falsy values, understands that arrays and objects stay truthy even when empty, and avoids validation that accidentally rejects valid values such as zero, false, or an empty string.

Common interview mistakes

A common mistake is assuming that empty arrays or empty objects are falsy. They are truthy. Another mistake is using if (!value) as a general missing value test when zero, false, or an empty string can be valid. Developers may also assume that all objects are truthy without knowing about the browser legacy exception document.all. For normal application objects, arrays, and functions, the truthy rule still applies. Validation should name the exact values that are invalid.

Interview tip

Start by naming the falsy values. Then point out that empty arrays and empty objects are truthy. Finish with the practical lesson that if (!value) can reject valid zero, false, or empty string input, so production validation should check the exact invalid values.

Interviewer may ask next
Are empty arrays and empty objects falsy because they contain no values?

No. Empty arrays and empty objects are truthy. Their Boolean conversion does not depend on how many elements or properties they contain. This matters because if ([]) and if ({}) both enter the true branch. If the application needs to know whether an array has elements, it should check array.length. If it needs to know whether an object has relevant data, it should check the required properties or another rule that matches the application.

When should you use an explicit null or undefined check instead of if (!value)?

Use an explicit null or undefined check when other falsy values can still be valid data. For example, zero may be a valid quantity, false may be a valid setting, and an empty string may be an allowed text value. Checking value === null || value === undefined rejects only the two missing values. The tradeoff is slightly more code, but the rule is precise, easier to review, and less likely to reject valid production input.

27. What does the `typeof` operator return for common JavaScript values?Language SpecificEasy

Question Details

Evaluate the expected typeof result for a string, number, bigint, boolean, symbol, undefined value, function, array, plain object, and null. Explain the two historical surprises—arrays reporting as objects and null reporting as 'object'—and name reliable checks for arrays and null.

Short Interview Answer (30-60 seconds)

The typeof operator returns a string that describes the basic runtime type of a JavaScript value. A string returns string, a number returns number, a bigint returns bigint, a boolean returns boolean, a symbol returns symbol, undefined returns undefined, and a function returns function. Arrays and plain objects both return object. Null also returns object because of historical JavaScript behavior. I use Array.isArray(value) to detect arrays and value === null to detect null.

Detailed Explanation

See the Code while reading this explanation.

JavaScript can inspect a value while a program is running and return a word that describes its basic kind. Most common values have clear results. Text, numbers, true or false values, symbols, missing undefined values, and functions each return a useful type name. Two cases need extra care. An array is reported as an object, so another check is needed to know that it is specifically an array. Null is also reported as an object because of an old JavaScript behavior. Developers therefore use separate checks for arrays and null when exact identification matters.

Useful Questions to Ask the Interviewer
  1. Would you like me to list the exact typeof result for every value in the question?
  2. Should I also explain the reliable checks for arrays and null?
What does the `typeof` operator return for common JavaScript values? diagram
How to Explain It in an Interview

typeof is a JavaScript operator. It returns a string that describes the basic runtime type of a value.

For a string, typeof "hello" returns "string". For a number, typeof 42 returns "number". For a bigint, typeof 42n returns "bigint". For a boolean, typeof true returns "boolean". For a symbol, typeof Symbol("id") returns "symbol". For undefined, typeof undefined returns "undefined". For a function, typeof function example() {} returns "function".

A plain object returns "object". An array also returns "object" because arrays are specialized JavaScript objects. If I need to know whether a value is specifically an array, I use Array.isArray(value).

Null is the other important surprise. typeof null returns "object". This result comes from historical JavaScript behavior and remains for compatibility. It does not mean that null is actually an object. To identify null exactly, I use value === null.

In production code, typeof is useful for simple runtime checks such as checking for strings, numbers, functions, symbols, bigints, booleans, and undefined values. I do not rely on it alone for arrays or null. These checks have negligible practical performance and memory cost in normal frontend code.

Example

The example applies typeof to each common value in the question and prints the exact runtime result. It then demonstrates the two important special cases. An array produces "object", so Array.isArray is used to identify it reliably. Null also produces "object", so strict equality with null is used to identify null exactly.

Code
const values = {
  stringValue: 'hello',
  numberValue: 42,
  bigintValue: 42n,
  booleanValue: true,
  symbolValue: Symbol('id'),
  undefinedValue: undefined,
  functionValue: function example() {},
  arrayValue: [1, 2, 3],
  objectValue: { name: 'Ada' },
  nullValue: null,
};

// Print the typeof result for each common value so the runtime behavior is easy to compare.
for (const [name, value] of Object.entries(values)) {
  console.log(name, typeof value);
}

// typeof reports arrays as object, so use the dedicated array check when exact identification matters.
console.log('array check', Array.isArray(values.arrayValue));

// typeof reports null as object, so use strict equality to identify null exactly.
console.log('null check', values.nullValue === null);
Where it is used

This behavior is useful when code needs simple runtime checks for values from user input, configuration, browser APIs, parsed data, or reusable functions. For example, code may verify that a callback is a function before calling it or confirm that a value is a string before using string operations. When an array is required, production code should use Array.isArray(value). When null needs special handling, code should use value === null.

Why Interviewers Ask This

Interviewers ask this question to check whether I understand how JavaScript reports the basic runtime type of common values. They also want to see whether I know the special results for arrays and null, and whether I can choose reliable checks when typeof alone is not specific enough.

Common interview mistakes

A common mistake is assuming that typeof returns a unique result for every JavaScript value. It does not. Arrays return "object", so checking typeof value === "array" is incorrect. Null also returns "object", so a check for only typeof value === "object" can include null as well as actual objects. Another mistake is forgetting that functions return "function". Use Array.isArray(value) for arrays and value === null for null.

Interview tip

Start by listing the normal typeof results. Then call out the two important surprises. Explain that arrays return "object" and null also returns "object". Finish with the reliable checks: Array.isArray(value) for arrays and value === null for null.

Interviewer may ask next
What does `typeof null` return, and how should you reliably check for null?

typeof null returns "object". This is historical JavaScript behavior that remains for compatibility. Null is not actually an object. When I need to identify null exactly, I use value === null. This matters because a check such as typeof value === "object" also matches arrays and plain objects.

Should you use `typeof` to detect whether a value is an array in production code?

No. typeof reports an array as "object", so it cannot distinguish an array from a plain object. I use Array.isArray(value) when the code specifically requires an array. This is the reliable built in check and makes the intent clear without adding a meaningful performance or memory cost in normal frontend code.

28. How do JavaScript primitive values differ from objects when assigned or passed to a function?Language SpecificEasy

Question Details

Use a browser JavaScript example with one number and one nested object. Show what happens when a function reassigns the parameter and when it mutates an object property. Distinguish copying a primitive value from copying an object reference, and clarify that JavaScript uses pass-by-value for both kinds of arguments.

Short Interview Answer (30-60 seconds)

JavaScript passes every argument by value. With a primitive such as a number, JavaScript copies the primitive value, so changing the parameter does not change the original variable. With an object, JavaScript copies the reference value. Both references can point to the same object, so mutating a property through the parameter can change that shared object. Reassigning the parameter to another object still does not change the caller variable.

Detailed Explanation

See the Code while reading this explanation.

The practical rule is simple. A number gives a function its own copied value. Changing that local value does not change the number outside the function. An object works differently because the copied value tells JavaScript where the same object is stored. This means the function can change something inside that shared object. However, if the function makes its local name point somewhere else, the outside name stays unchanged. This difference matters because object changes can be visible in other parts of an application that use the same object.

Useful Questions to Ask the Interviewer
  1. Should I show both changing the parameter itself and changing a property inside the object?
  2. Should I include a nested property to demonstrate that the same object is shared?
How do JavaScript primitive values differ from objects when assigned or passed to a function? diagram
How to Explain It in an Interview

JavaScript always passes function arguments by value. The important detail is what that value contains.

For a primitive such as a number, the value itself is copied. If count is 10 and we call a function with count, the function receives its own value of 10. If the function later assigns 99 to its parameter, only that local parameter changes. The original count remains 10.

For an object, the value being copied is a reference to the object. A reference is a value that lets JavaScript reach the object. The caller variable and the function parameter therefore contain separate copies of a reference that points to the same object.

This explains two different behaviors. If the function changes person.details.score, it changes the shared object, so the caller can see the new score. If the function instead assigns a completely new object to its parameter, only the local parameter receives the new reference. The caller variable still points to the original object.

The same rule applies during normal assignment. Assigning one primitive variable to another copies the primitive value. Assigning one object variable to another copies the reference value, not the whole object.

This matters in frontend code because shared objects are common in application state, configuration, and data passed between functions. Accidental mutation can create unexpected changes. A shallow copy such as object spread creates a new outer object, but nested objects can still be shared. A deep copy requires a separate operation such as structuredClone when the data is supported. JavaScript is not pass by reference. It is pass by value in both cases.

Example

The example uses one number and one object with a nested details object. The number parameter is reassigned, but the original number stays unchanged because the primitive value was copied. The object parameter is also reassigned, and that reassignment does not change the caller variable because only the local copied reference changes. Before that reassignment, the function changes details.score. That mutation is visible outside the function because the copied reference and the caller reference point to the same object.

Code
const count = 10;
const person = {
  name: 'Maya',
  details: {
    score: 5,
  },
};

function changeNumber(value) {
  // Reassign only the local copy of the primitive value.
  value = 99;
  console.log('Inside changeNumber:', value);
}

function changeObject(value) {
  // Mutate the shared object reached through the copied reference.
  value.details.score = 20;

  // Replace only the local copy of the reference with a new reference.
  value = {
    name: 'New person',
    details: {
      score: 100,
    },
  };

  console.log('Inside changeObject:', value);
}

changeNumber(count);
changeObject(person);

// The original primitive is still 10 because only its copied value changed.
console.log('Outside count:', count);

// The nested score is 20 because the function mutated the shared object.
// The name is still Maya because reassigning the parameter did not replace person.
console.log('Outside person:', person);
Where it is used

This behavior appears whenever frontend code passes values into helper functions, event handlers, state utilities, data transformation functions, and browser application logic. It is especially important when several parts of the code share the same object. Understanding the rule helps developers decide whether a function should intentionally mutate an object or create a separate copy before changing data.

Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands what JavaScript actually copies during assignment and function calls. They want to see whether the candidate can separate parameter reassignment from object mutation and can explain why changing a nested object property may affect the original object.

Common interview mistakes

A common mistake is saying that objects are passed by reference. JavaScript passes the reference value by value. Another mistake is assuming that assigning a new object to a function parameter replaces the caller variable. It does not. Developers also sometimes think an object spread automatically copies every nested object. It makes a shallow copy, so nested objects may still be shared.

Interview tip

Start with the sentence that JavaScript always passes arguments by value. Then explain that a primitive value is copied directly, while an object reference value is copied. Use one example to separate parameter reassignment from object mutation. That distinction is the key point the interviewer is testing.

Interviewer may ask next
What happens if the function reassigns the object parameter before it mutates a property?

The caller object will not be affected by mutations made after that reassignment. Reassigning the parameter replaces only the local copied reference. After that point, the parameter points to a different object, so mutations through that parameter affect the new object rather than the original caller object. This matters because reassignment changes which object the local parameter can reach, but it never changes the caller variable itself.

How can you avoid accidentally changing a shared nested object in production code?

Create an independent copy before making changes when shared mutation is not wanted. A shallow object spread creates a new outer object, but nested objects are still shared unless they are copied too. For supported data, structuredClone can create a deep copy. The tradeoff is extra allocation and copying work, so the copying strategy should match the data size and the level of isolation the application actually needs.

29. How do template literals support interpolation and multiline strings?Language SpecificEasy

Question Details

Create a browser JavaScript example that formats a user's name and item count into a two-line message. Explain expression interpolation, embedded newlines, escaping a backtick, and the difference between a normal template literal and a tagged template without requiring implementation of a tag.

Short Interview Answer (30-60 seconds)

Template literals use backticks instead of normal quotes. I can place an expression inside ${} and JavaScript converts its result into text at that position. I can also put a real line break inside the literal, so multiline text is easy to read. If I need an actual backtick inside the text, I escape it with a backslash. A tagged template is different because JavaScript passes the template parts and expression values to a tag function instead of directly producing the normal string.

Detailed Explanation

See the Code while reading this explanation.

Template literals make it easier to build text that contains changing values or several lines. For example, a page can show a person's name and the number of items in one readable message. JavaScript places each requested value into the correct position. A line break written inside the text also stays as a line break in the result. This is useful for messages, labels, generated content, and other text where joining many small pieces would be harder to read. A special form can also send the text pieces and values to another function for custom handling.

Useful Questions to Ask the Interviewer
  1. Should the example preserve the line break exactly as written in the source?
  2. Do you want me to explain tagged templates conceptually without implementing a tag?
How do template literals support interpolation and multiline strings? diagram
How to Explain It in an Interview

A normal template literal is written between backticks. Expression interpolation uses ${expression}. JavaScript evaluates the expression, converts its result to a string for the substitution, and inserts that text at the expression position.

For example, if name is "Maya" and itemCount is 3, the template can produce two lines. The first line contains the name. The second line contains the item count. A real newline between those lines in the source becomes a newline in the resulting string.

A backtick normally closes the template literal. To include a literal backtick in its text, write it as \` so JavaScript treats it as content instead of the closing delimiter.

Template literals are useful when a frontend message combines fixed text with dynamic values or needs readable multiline text. They still create strings, so large repeated string construction can allocate new string data. They also do not make interpolated values safe for HTML. User supplied text should be inserted with safe DOM APIs when it is rendered on a page.

A tagged template changes the evaluation process. A tag function receives the fixed string parts and the evaluated expression values. The tag decides what value to return. A normal template literal directly produces the combined string. Tagged templates are useful when custom processing is needed, but they are unnecessary for ordinary interpolation.

Example

The example stores a user name and item count, then creates one normal template literal. It interpolates both values and keeps a real newline between the two message lines. It also escapes a backtick so that character becomes part of the final string. The example then prints the result. A tagged template is only explained because the question does not require implementing a tag.

Code
const name = 'Maya';
const itemCount = 3;

// Build one readable message with two interpolated values and a real newline.
const message = `Hello, ${name}!
You have ${itemCount} items. Use a \`backtick\` when needed.`;

// Print the exact two line result so the newline behavior is visible.
console.log(message);
Where it is used

Template literals are commonly used for frontend messages, logging text, generated labels, URLs, small HTML related strings, test descriptions, and other text that combines fixed words with changing values. Multiline literals are useful when the source should visually match the final text. For actual page rendering, user supplied values should still be inserted with safe DOM APIs rather than treating an interpolated string as trusted HTML.

Why Interviewers Ask This

Interviewers ask this to check whether I understand how template literals build strings, how values are inserted with expressions, how line breaks are preserved, and when tagged templates behave differently from normal template literals. It also tests whether I can use the syntax safely in real frontend code.

Common interview mistakes

A common mistake is using normal single or double quotes and expecting ${} to interpolate a value. Another mistake is forgetting that an unescaped backtick closes the template literal. Developers may also expect indentation inside a multiline template to disappear, but spaces and line breaks written inside the template become part of the string. Another mistake is assuming a tagged template is only another spelling for interpolation. A tag receives the template parts and values and can return something other than the normal combined string. Interpolating user input also does not automatically make HTML output safe.

Interview tip

Start by saying that backticks enable both ${} interpolation and literal line breaks. Then show one small two line example. Mention how to escape a backtick, and finish by explaining that a normal template produces the combined string while a tagged template gives the parts and values to a tag function.

Interviewer may ask next
What happens to spaces and line breaks that are written inside a multiline template literal?

They become part of the resulting string. JavaScript preserves the characters written inside the template, including newline characters and indentation spaces. This matters because formatting added only to make source code look neat can also appear in displayed or logged text. If exact output matters, I should place the template carefully or process the resulting string deliberately.

When would you use a tagged template instead of a normal template literal?

I would use a tagged template when custom processing of the fixed text and expression values is required. JavaScript passes those parts to the tag function, and the tag controls the returned value. This can support library features such as specialized formatting or transformation. For an ordinary message like the name and item count example, a normal template literal is simpler and clearer because no custom processing is needed.

30. How do array and object destructuring assignments work?Language SpecificEasy

Question Details

Given a coordinate array and a user object with a nested address, demonstrate positional array destructuring, property-name object destructuring, renaming, default values, rest collection, and safely handling a missing nested object. Keep the example runnable in a modern browser script.

Short Interview Answer (30-60 seconds)

Destructuring lets me take values from arrays or objects and assign them to variables in one statement. Array destructuring uses position, while object destructuring uses property names. I can also rename object properties, provide defaults for undefined values, collect remaining values with rest syntax, and use a default empty object when a nested object may be missing.

Detailed Explanation

See the Code while reading this explanation.

Destructuring is a short way to take useful pieces from a group of values and give those pieces clear names. For a list, the first name receives the first value, the second name receives the second value, and so on. For a named group, each name looks for a matching property. You can also choose a different local name, provide a fallback when a value is missing, gather the remaining values, and avoid an error when a deeper group does not exist. This keeps common data reading code shorter and easier to follow.

Useful Questions to Ask the Interviewer
  1. Should the example show both a present address and a missing address case?
  2. Should the remaining user properties be collected into another object?
How do array and object destructuring assignments work? diagram
How to Explain It in an Interview

Array destructuring reads values by position. With const [x, y, ...remainingCoordinates] = coordinates, x receives the first array element, y receives the second, and the rest syntax creates a new array containing any remaining elements.

Object destructuring reads properties by property name. With const { name, role: jobRole = "Guest", ...otherUserData } = user, JavaScript reads name, reads role into a local variable named jobRole, uses "Guest" only when role is undefined, and creates a new object containing the remaining own enumerable properties that were not already selected.

Nested destructuring can fail if the value being unpacked is undefined or null. A safe pattern is const { address: { city = "Unknown" } = {} } = user. If address is undefined, JavaScript uses the empty object for the nested pattern, so reading city is safe. This default does not help if address is explicitly null.

Destructuring does not deep copy nested objects. If an extracted value is an object, the new variable still contains a reference to that same object. Rest collection creates a new outer array or object, but nested objects remain shared references.

Rest collection also has a cost. JavaScript must create a new array or object and copy the remaining values or properties into it. The work and extra memory grow with the amount collected. Simple destructuring without rest does not create a copy of the whole source collection.

In production frontend code, destructuring is useful for component data, function results, configuration objects, and validated server data. It works best when the expected shape is clear. Avoid deeply nested patterns when they make code difficult to read or when incoming data has not been validated.

Example

The example uses one coordinate array and one user object. The coordinate array demonstrates positional destructuring and rest collection. The user object demonstrates property name matching, renaming, a default value, object rest collection, and nested destructuring. A second user value has no address property and shows how = {} makes the missing nested object safe. The fallback for the nested object applies when address is undefined. It does not protect against an explicit null address. The rest variables create new outer collections, while nested object values remain shared references.

Code
const coordinates = [40.7, -74.0, 15, 25];

// Array destructuring reads values by position.
// The rest variable receives a new array with the remaining elements.
const [latitude, longitude, ...remainingCoordinates] = coordinates;

const user = {
  name: 'Maya',
  role: undefined,
  age: 28,
  address: {
    city: 'Chicago',
  },
};

// Object destructuring reads by property name.
// role is renamed to jobRole, and the default is used because role is undefined.
// otherUserData receives a new outer object containing the remaining properties.
const { name, role: jobRole = 'Guest', ...otherUserData } = user;

// The nested pattern reads city from address.
// The empty object default makes the pattern safe when address is undefined.
const { address: { city = 'Unknown' } = {} } = user;

const userWithoutAddress = {
  name: 'Noah',
};

// Because address is missing, JavaScript uses the empty object before reading city.
// cityWithoutAddress then uses its own default value.
const { address: { city: cityWithoutAddress = 'Unknown' } = {} } = userWithoutAddress;

console.log(latitude, longitude);
console.log(remainingCoordinates);
console.log(name, jobRole);
console.log(otherUserData);
console.log(city);
console.log(cityWithoutAddress);
Where it is used

Destructuring is common when reading component properties, configuration values, function return values, browser API results, and validated server response objects. It is especially useful when only a few values are needed from a larger object or array. Defaults help with optional values. Rest collection is useful when some properties are handled directly and the remaining properties must be passed or processed together.

Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands how JavaScript reads values from arrays and objects, how position differs from property name, and how renaming, defaults, rest collection, and missing nested data behave. It also shows whether the candidate can write concise code without making unsafe assumptions about incoming frontend data.

Common interview mistakes

A common mistake is thinking array destructuring uses property names. It uses position. Another mistake is thinking an object default runs for every false like value. A destructuring default runs only when the extracted value is undefined. Developers also sometimes expect = {} to protect nested destructuring from null, but it does not. Another mistake is assuming object or array rest creates a deep copy. The new outer collection is separate, but nested object references are still shared.

Interview tip

Explain the rule in this order: arrays use position, objects use property names, renaming changes only the local variable name, defaults apply to undefined, rest collects what remains, and a default empty object can protect a nested pattern when the nested property is missing.

Interviewer may ask next
What happens if the nested address property is null instead of undefined?

The nested destructuring pattern throws an error because the = {} default is used only when address is undefined. An explicit null value remains null, and JavaScript cannot destructure city from null. This matters when incoming data can contain explicit null values. In production code, validate or normalize that value before nested destructuring.

Does rest destructuring make a deep copy of the remaining values?

No. Rest destructuring creates a new outer array or object, but nested object values are not deeply copied. If a collected value is an object, the new collection contains a reference to that same object. This matters because later mutation of that nested object can also be observed through the original source. Rest collection also requires time and memory to create and fill the new outer collection.

More questions load as you scroll

Disclaimer: This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.