🎯 Free Interview Preparation

JavaScript Interview Questions & Answers

Top JavaScript interview questions and answers for freshers and experienced developers.

JavaScript Interview Questions & Answers (2026)

Top JavaScript interview questions and answers for freshers and experienced developers.

Junior Question: What’s the difference between `null` and `undefined`?

Level: Junior | Category: Fundamentals | Time: 5 mins

Expected Answer

  • `undefined` means a value was not provided / missing.
  • `null` is an explicit “no value” assignment.
  • `typeof undefined` → `"undefined"`, `typeof null` → `"object"`.

Junior Question: What is the difference between `let` and `const`?

Level: Junior | Category: Fundamentals | Time: 5 mins

Expected Answer

  • `let` can be reassigned.
  • `const` cannot be reassigned (but objects/arrays can still be mutated).
  • Both are block scoped.

Practical Example

const person = { name: 'Ava' };
person.name = 'Noah'; // ok (mutation)
person = { name: 'Mia' }; // error (reassign)

Junior Question: What is hoisting?

Level: Junior | Category: Fundamentals | Time: 5 mins

Expected Answer

Declarations are moved to the top of their scope before code runs. For variables declared with `var`, initialization becomes `undefined`. `let`/`const` are in the temporal dead zone until initialized.

Junior Question: Difference between `==` and `===`?

Level: Junior | Category: Fundamentals | Time: 5 mins

Expected Answer

  • `==` performs type coercion before comparison.
  • `===` checks type and value without coercion.
  • Prefer `===` for predictable behavior.

Junior Question: What does `typeof` return for `null`, `undefined`, and an array?

Level: Junior | Category: Fundamentals | Time: 5 mins

Expected Answer

  • `typeof null` → `"object"`
  • `typeof undefined` → `"undefined"`
  • `typeof []` → `"object"` (use `Array.isArray` to detect arrays)

Junior Question: What’s the difference between `function` and `arrow` functions?

Level: Junior | Category: Fundamentals | Time: 5 mins

Expected Answer

  • Arrow functions don’t have their own `this` (they capture from outer scope).
  • Arrow functions don’t have `arguments`.
  • Arrow functions can’t be used as constructors (`new`).

Mid-Level Question: Explain closures with an example.

Level: Mid-Level | Category: Fundamentals | Time: 5 mins

Expected Answer

A closure is a function that retains access to variables from its outer scope even after the outer function has finished.

Practical Example

function makeCounter() {
  let count = 0;
  return function () {
    count++;
    return count;
  };
}

const c = makeCounter();
console.log(c()); // 1
console.log(c()); // 2

Mid-Level Question: What is the prototype chain?

Level: Mid-Level | Category: Fundamentals | Time: 5 mins

Expected Answer

  • Objects can inherit properties via `[[Prototype]]`.
  • If a property isn’t found on the object, JS checks its prototype, then prototype of the prototype, etc.
  • This is how inheritance and method lookup work in JS.

Mid-Level Question: What’s the difference between `Object.assign` and the spread operator?

Level: Mid-Level | Category: Fundamentals | Time: 5 mins

Expected Answer

  • Both do shallow copies.
  • Spread and `Object.assign` handle property copying similarly, but spread syntax can be clearer for merging.
  • Both copy enumerable own properties.

Mid-Level Question: Debounce vs throttle?

Level: Mid-Level | Category: Fundamentals | Time: 5 mins

Expected Answer

  • Debounce: delays execution until events stop firing.
  • Throttle: limits execution to once per time window.
  • Debounce is common for search-after-typing; throttle for scroll/resize.

Tricky Question: Predict the output

Level: Tricky Question | Category: Fundamentals | Time: 5 mins

Code Example

console.log('A');

setTimeout(() => console.log('B'), 0);

Promise.resolve()
  .then(() => console.log('C'))
  .then(() => console.log('D'));

console.log('E');

Answer

A, E, C, D, B

Tricky Question: What does this print?

Level: Tricky Question | Category: Fundamentals | Time: 5 mins

Code Example

var x = 10;

function test() {
  console.log(x);
  let x = 20;
}

test();

Answer

It throws a ReferenceError due to the temporal dead zone for the inner `let x`.

Tricky Question: Predict the output

Level: Tricky Question | Category: Fundamentals | Time: 5 mins

Code Example

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}

Answer

3, 3, 3 (because `var` is function-scoped and `i` ends as 3).

Tricky Question: Predict the output

Level: Tricky Question | Category: Fundamentals | Time: 5 mins

Code Example

const p = Promise.resolve('X');

p.then(v => {
  console.log(v);
  return 'Y';
}).then(v => console.log(v));

console.log('Z');

Answer

Z, X, Y

Production Incidents Question: Memory Usage Grows Over Time (No Page Reload)

Level: Production Incident | Category: Production Incidents | Time: 15 mins

Scenario

How would you investigate?

Expected Answer

  • Identify when growth starts and reproduce with a profiling run
  • Heap snapshots (baseline → after user flows → later) and compare retained size
  • Look for detached DOM nodes and growing object types
  • Check event listeners (accidental re-binding), timers, and intervals
  • Confirm leaks vs caching by validating object retention paths

Production Incidents Question: Browser Freezes Intermittently After User Interaction

Level: Production Incident | Category: Production Incidents | Time: 15 mins

Scenario

What do you do first?

Expected Answer

  • Record a Performance profile while reproducing
  • Find long tasks (main thread blocking), GC pauses, and forced reflows
  • Check for expensive renders, large JSON parsing, and synchronous loops
  • Use flame chart to locate top CPU consumers
  • Mitigate: chunk work, debounce/throttle, virtualize lists, move work off main thread

Production Incidents Question: Production Bug: “Button Click Works, But UI Doesn’t Update”

Level: Production Incident | Category: Production Incidents | Time: 15 mins

Scenario

How would you narrow down the cause?

Expected Answer

  • Confirm event fires and handler runs (logging/Breakpoints)
  • Verify state/store update occurs and UI is subscribed/reacting
  • Check immutability assumptions (mutating in place vs replacing references)
  • Inspect async races: stale closures, out-of-order responses
  • Use network traces + source maps + tracing spans to verify ordering

Production Incidents Question: Client-Side Timeouts Only on High-Latency Networks

Level: Production Incident | Category: Production Incidents | Time: 15 mins

Scenario

What’s your debugging approach?

Expected Answer

  • Reproduce with throttling (network conditions) and vary latency/bandwidth
  • Inspect fetch/XHR retry logic and timeout settings
  • Validate request sequencing and cancellation (AbortController)
  • Check for large payloads, slow parsing, and JSON reviver overhead
  • Confirm server responses are timely and not hanging under load

Production Incidents Question: “Unhandled Promise Rejection” Spikes in Sentry

Level: Production Incident | Category: Production Incidents | Time: 15 mins

Scenario

How would you respond?

Expected Answer

  • Classify the error type and attach context (request ID, user flow)
  • Find where the promise is created vs where it’s awaited
  • Add missing `catch` or ensure awaited calls are inside try/catch
  • Check for swallowed errors in `async` functions and event handlers
  • Fix root logic and add regression coverage

Production Incidents Question: Intermittent “Cannot read property X of undefined”

Level: Production Incident | Category: Production Incidents | Time: 15 mins

Scenario

How do you investigate?

Expected Answer

  • Reproduce by correlating with specific routes/actions and device types
  • Instrument with guard logs (input shape, timing, feature flags)
  • Look for race conditions (async updates after component unmount)
  • Check for optional fields and partial API responses
  • Fix by validating data contracts and improving state initialization

Production Incidents Question: Layout Jank When Scrolling

Level: Production Incident | Category: Production Incidents | Time: 15 mins

Scenario

What metrics and tools do you use?

Expected Answer

  • Use Performance panel to find layout thrashing and long tasks
  • Inspect rendering: images, reflows, style recalculation triggers
  • Check for expensive scroll handlers (missing passive listeners)
  • Use transforms/opacity over top/left where possible
  • Virtualize long lists and avoid measuring DOM repeatedly

Production Incidents Question: Event Handlers Trigger Multiple Times

Level: Production Incident | Category: Production Incidents | Time: 15 mins

Scenario

How would you debug?

Expected Answer

  • Confirm binding logic: are handlers re-attached on rerenders?
  • Check for missing cleanup in effects (removeEventListener / clearTimeout)
  • Look for duplicate components mounting/unmounting
  • Verify framework-specific patterns (dependency arrays, keys)
  • Use breakpoints on event listener registration to track the source

Anti-AI Detection Question: Explain the hardest JavaScript bug you fixed.

Level: Anti-AI Validation | Category: Anti-AI Detection | Time: 12 mins

Expected Answer

Walk through:

  • Root Cause
  • Investigation
  • Tools Used
  • Final Fix

Anti-AI Detection Question: Production Outage: Users See Stale Data

Level: Anti-AI Validation | Category: Anti-AI Detection | Time: 12 mins

Expected Answer

Explain the debugging and mitigation plan.

  • Identify impact window and affected user segments
  • Check caching layers: CDN, browser cache, service worker, app state
  • Inspect network responses and headers (ETag/Cache-Control)
  • Verify cache invalidation and versioning logic
  • Hotfix: force refresh, bump cache keys, rollback if needed

Anti-AI Detection Question: Race Condition: The UI Shows an Older Result

Level: Anti-AI Validation | Category: Anti-AI Detection | Time: 12 mins

Expected Answer

How do you detect and fix it?

  • Reproduce by changing input quickly or simulating latency
  • Capture request/response order and correlate with UI state
  • Use request cancellation (AbortController) and ignore stale responses
  • Ensure state updates are based on latest input version/token
  • Add tests for out-of-order resolution

Anti-AI Detection Question: Security/Injection Issue: XSS Risk in Rendering

Level: Anti-AI Validation | Category: Anti-AI Detection | Time: 12 mins

Expected Answer

What’s your response workflow?

  • Identify where untrusted input enters the DOM
  • Confirm whether `innerHTML`/dangerous rendering is used
  • Replace with safe rendering or sanitize appropriately
  • Add security tests and validate CSP configuration
  • Patch and audit other similar code paths

Anti-AI Detection Question: “Works Locally, Fails in Production” (Only in Some Browsers)

Level: Anti-AI Validation | Category: Anti-AI Detection | Time: 12 mins

Expected Answer

How do you systematically debug?

  • Compare environments: build flags, polyfills, bundling output
  • Use browser-specific console errors + source maps
  • Check for missing polyfills and unsupported features
  • Validate CORS, CSP, and asset loading differences
  • Reproduce using production build and matching browser versions

Anti-AI Detection Question: Async Logic: Requests Keep Spinning After Navigation

Level: Anti-AI Validation | Category: Anti-AI Detection | Time: 12 mins

Expected Answer

What causes this and how do you fix it?

  • Requests not canceled on unmount/navigation
  • State updates after unmount causing errors or retries
  • Use AbortController and cancellation-safe state management
  • Guard against stale updates using mounted flags/version tokens
  • Add integration test around navigation/unmount

Anti-AI Detection Question: Data Consistency: “Counts” Don’t Match Backend

Level: Anti-AI Validation | Category: Anti-AI Detection | Time: 12 mins

Expected Answer

How do you investigate?

  • Confirm source of truth (backend vs client computed)
  • Verify transport: request parameters, filters, pagination, units
  • Check optimistic UI and reconciliation logic
  • Inspect timezones and rounding/format conversions
  • Write a minimal reproduction with captured inputs and responses

Anti-AI Detection Question: Stuck Loading State (Spinner Never Stops)

Level: Anti-AI Validation | Category: Anti-AI Detection | Time: 12 mins

Expected Answer

What questions do you ask and what do you check?

  • Is the loading state set/reset in all code paths (success, error, timeout)
  • Are promises always awaited or are rejections uncaught?
  • Is there a missing `finally` block for async flows?
  • Check for conditional rendering gates that block completion
  • Audit telemetry: correlate loading start/finish events

Anti-AI Detection Question: Large Bundle / Slow First Load

Level: Anti-AI Validation | Category: Anti-AI Detection | Time: 12 mins

Expected Answer

How do you diagnose and improve it?

  • Use Lighthouse/Web Vitals + bundle analyzer
  • Identify heavy dependencies and unused exports
  • Apply code splitting and lazy loading on route boundaries
  • Optimize assets (compression, image formats, caching)
  • Reduce blocking scripts and confirm improvements in RUM

Anti-AI Detection Question: Infinite Scroll Fetches Too Much (Rate/Cost Spike)

Level: Anti-AI Validation | Category: Anti-AI Detection | Time: 12 mins

Expected Answer

What’s your approach?

  • Inspect scroll trigger conditions and thresholds
  • Check for duplicate fetches and missing “in-flight” guards
  • Ensure dedupe by request key and cancel older requests
  • Backoff/retry strategy and server-side limits
  • Add monitoring for requests per session and latency percentiles

Junior Question: Predict the output

Level: Junior | Category: Code Predictions | Time: 5 mins

Code Example

console.log(typeof null);
console.log(typeof undefined);
console.log(typeof []);

Answer

object, undefined, object

Junior Question: Predict the output

Level: Junior | Category: Code Predictions | Time: 5 mins

Code Example

console.log(0 == false);
console.log(0 === false);
console.log('0' == 0);
console.log('0' === 0);

Answer

true, false, true, false

Junior Question: What does this print?

Level: Junior | Category: Code Predictions | Time: 5 mins

Code Example

let a = 1;

if (true) {
  let a = 2;
  console.log(a);
}

console.log(a);

Answer

2 then 1

Mid-Level Question: Predict the output (Promises + ordering)

Level: Mid-Level | Category: Code Predictions | Time: 5 mins

Code Example

console.log('start');

queueMicrotask(() => console.log('micro 1'));

Promise.resolve()
  .then(() => console.log('promise then'))
  .then(() => console.log('promise then 2'));

setTimeout(() => console.log('timeout'), 0);

console.log('end');

Answer

start, end, micro 1, promise then, promise then 2, timeout

Mid-Level Question: Predict the output (async/await)

Level: Mid-Level | Category: Code Predictions | Time: 5 mins

Code Example

async function run() {
  console.log(1);
  await Promise.resolve();
  console.log(2);
}
run();
console.log(3);

Answer

1, 3, 2

Tricky Question: Predict the output (var in loops)

Level: Tricky Question | Category: Code Predictions | Time: 5 mins

Code Example

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 10);
}
console.log('done');

Answer

done then 3, 3, 3

Tricky Question: Predict the output (scope + TDZ)

Level: Tricky Question | Category: Code Predictions | Time: 5 mins

Code Example

console.log(a);
let a = 1;

Answer

ReferenceError (accessing `a` before initialization)

Tricky Question: Predict the output (this binding)

Level: Tricky Question | Category: Code Predictions | Time: 5 mins

Code Example

const obj = {
  x: 10,
  f: function () {
    setTimeout(function () {
      console.log(this.x);
    }, 0);
  }
};

obj.f();

Answer

undefined (or error depending on environment), because `this` inside the normal function isn’t `obj`.

Tricky Question: Fix the code (use arrow function)

Level: Tricky Question | Category: Code Predictions | Time: 5 mins

Code Example

const obj = {
  x: 10,
  f: function () {
    setTimeout(function () {
      console.log(this.x);
    }, 0);
  }
};

Answer

const obj = {
  x: 10,
  f: function () {
    setTimeout(() => {
      console.log(this.x);
    }, 0);
  }
};

Advanced Concepts Question: What is the difference between event bubbling and event capturing?

Level: Mid-Level | Category: Advanced Concepts | Time: 10 mins

Expected Answer

  • Capturing: event travels from window/document down to target.
  • Bubbling: event travels from target up to ancestors.
  • Default is bubbling; capturing can be enabled with
    { capture: true }
    .
  • `event.stopPropagation()` stops further propagation.

Advanced Concepts Question: Explain what “task queue” and “microtask queue” are.

Level: Mid-Level | Category: Advanced Concepts | Time: 10 mins

Expected Answer

  • Task queue handles macrotasks (e.g., `setTimeout`, `setInterval`).
  • Microtask queue handles microtasks (e.g., `Promise.then`, `queueMicrotask`).
  • Microtasks run after the current call stack, before the next task.
  • This ordering affects promise vs timeout output.

Advanced Concepts Question: What is the difference between synchronous and asynchronous errors?

Level: Mid-Level | Category: Advanced Concepts | Time: 10 mins

Expected Answer

  • Synchronous: caught by surrounding try/catch in the same call stack.
  • Asynchronous: must be handled where the promise is resolved/rejected (then/catch) or awaited in try/catch.
  • Uncaught async errors often appear as unhandled promise rejections.

Advanced Concepts Question: How would you implement a “safe async” wrapper?

Level: Mid-Level | Category: Advanced Concepts | Time: 10 mins

Expected Answer

function safeAsync(fn) {
  return async (...args) => {
    try {
      return await fn(...args);
    } catch (err) {
      // log, report, or return a fallback
      console.error('safeAsync caught:', err);
      throw err; // or return { ok:false, err }
    }
  };
}

Question #46: Explain Primitive vs Reference types in JavaScript. How are they stored in memory (Stack vs Heap)?

Level: Junior | Category: Memory Management | Time: 5 mins

Expected Answer

JavaScript variables can hold two categories of values:

  • Primitive Types: Numbers, Strings, Booleans, null, undefined, Symbols, and BigInt. They are immutable and stored directly on the Stack because they have fixed memory sizes.
  • Reference Types: Objects, Arrays, Functions, Maps, and Sets. They are mutable and stored in the Heap because their sizes are dynamic. The variable on the Stack only holds a reference (pointer) to the heap memory address.

Code Example


// Primitive value copy
let a = 10;
let b = a;
b = 20; // 'a' is still 10

// Reference value copy
let obj1 = { name: 'Alice' };
let obj2 = obj1;
obj2.name = 'Bob'; // obj1.name is now 'Bob' because both share the same reference!
Interviewer Note: 💡 Check if the candidate knows that stack accesses are faster than heap lookups, which require resolving pointer references.

Question #47: What is the difference between shallow copy and deep copy? Provide a code example.

Level: Junior | Category: Functional Programming | Time: 5 mins

Expected Answer

Copying objects can be done at different depths:

  • Shallow Copy: Copies the top-level properties. If a property is a reference type (like a nested object), it only copies the reference pointer, meaning the copy shares nested objects with the original. Common methods: spread operator {...obj}, Object.assign().
  • Deep Copy: Recursively copies all properties, creating entirely new objects in memory for all nested layers. Common methods: structuredClone() (modern ES), JSON.parse(JSON.stringify(obj)) (has limitations).

Code Example


const original = { name: 'Alice', details: { age: 30 } };

// Shallow Copy
const shallow = { ...original };
shallow.details.age = 40; // original.details.age is now 40!

// Deep Copy
const deep = structuredClone(original);
deep.details.age = 50; // original.details.age is still 40!
Interviewer Note: 💡 Ask about limitations of JSON serialization (loses functions, regexes, undefined values, and symbols).

Question #48: Explain ES Modules (import/export) vs CommonJS (require/exports).

Level: Junior | Category: Modules | Time: 5 mins

Expected Answer

JavaScript modules allow code segregation. The two main systems are:

  • ES Modules (ESM): The standard module format. Uses import and export statements. It is statically analyzed, resolving dependencies at compile time, which enables tree-shaking. Runs asynchronously.
  • CommonJS (CJS): The Node.js legacy module format. Uses require() and module.exports. It is dynamic, loading scripts synchronously at runtime.
Interviewer Note: 💡 Look for candidate's knowledge that ESM is native to browsers whereas CommonJS requires bundlers to run in browsers.

Question #49: Explain Optional Chaining (?.) and Nullish Coalescing (??). How do they differ from logical OR (||)?

Level: Junior | Category: Modern Syntax | Time: 5 mins

Expected Answer

Optional Chaining and Nullish Coalescing simplify nested property accesses and fallback assignments:

  • Optional Chaining (?.): Safely reads nested values. If a parent reference is null/undefined, it returns undefined instead of throwing a TypeError.
  • Nullish Coalescing (??): Returns the fallback value only when the left expression is nullish (null or undefined).
  • Comparison with OR (||): The OR operator returns the fallback for any falsy value (such as 0, "", or false), whereas ?? respects these values.

Code Example


const config = { count: 0, text: '' };

const count1 = config.count || 10; // returns 10 because 0 is falsy
const count2 = config.count ?? 10; // returns 0 because 0 is not nullish
Interviewer Note: 💡 Candidates should show clear code predictability benefits when using Nullish Coalescing instead of standard logical OR.

Question #50: What is Event Delegation in the browser DOM, and what are its performance benefits?

Level: Junior | Category: DOM & DOM APIs | Time: 5 mins

Expected Answer

Event Delegation is a pattern where you attach a single event listener to a parent element rather than attaching listeners to individual children. Due to Event Bubbling, events fired on child elements bubble up to parent containers where they can be intercepted.

  • Memory Savings: Reduces the number of event listeners in memory, which is critical for large tables or list elements.
  • Dynamic Elements: Automatically listens to new child elements added to the DOM dynamically without needing to bind listeners to them manually.

Code Example


// Single listener on parent ul container
document.getElementById('myList').addEventListener('click', (event) => {
  if (event.target.tagName === 'LI') {
    console.log('Item clicked:', event.target.innerText);
  }
});
Interviewer Note: 💡 A core DOM API concept. Make sure they mention event.target and event bubbling.

Question #51: What are the differences between Map/Set and standard Object/Array?

Level: Junior | Category: Data Structures | Time: 5 mins

Expected Answer

ES6 introduced Map and Set structures to resolve limitations in Objects and Arrays:

  • Map vs Object: Objects only support string or symbol keys; Maps support any data type as key (including objects, functions). Maps preserve insertion order and provide size properties out of the box.
  • Set vs Array: Arrays allow duplicate values and index-based lookups. Sets are collections of unique values, offering faster lookup times using set.has().
Interviewer Note: 💡 Good candidates discuss complexity: checking element presence in a Set is O(1), whereas in an Array it is O(N).

Question #52: Explain how bind, call, and apply work.

Level: Junior | Category: Functions | Time: 5 mins

Expected Answer

These methods are used to set the this context of a function call explicitly:

  • call(): Invokes the function immediately, passing arguments individually: fn.call(context, arg1, arg2).
  • apply(): Invokes the function immediately, passing arguments as an array: fn.apply(context, [arg1, arg2]).
  • bind(): Returns a new function instance with this permanently bound, allowing it to be called later.

Code Example


function greet(greeting, punctuation) {
  return `${greeting}, ${this.name}${punctuation}`;
}
const user = { name: 'Alice' };

// Immediate execution
greet.call(user, 'Hello', '!'); // 'Hello, Alice!'
greet.apply(user, ['Hi', '.']); // 'Hi, Alice.'

// Deferred execution
const bound = greet.bind(user);
bound('Welcome', '?'); // 'Welcome, Alice?'
Interviewer Note: 💡 Check if the candidate knows that arrow functions cannot have their 'this' context overridden by bind/call/apply.

Question #53: Explain Lexical Scope, Execution Context, and the Call Stack in JavaScript.

Level: Mid-Level | Category: Execution Context | Time: 7 mins

Expected Answer

These concepts define how variables are resolved and how functions execute:

  • Lexical Scope: The scoping model where variable visibility is determined by their physical location in the source code during compilation. Nested functions can access variables from outer scopes.
  • Execution Context: An environment created by the JS engine to execute code. It contains the local variables, the scope chain, and the this binding. It has two phases: creation (hoisting, scope building) and execution.
  • Call Stack: A LIFO stack that tracks active execution contexts. When a function is called, its context is pushed onto the stack. When it returns, it is popped off.
Interviewer Note: 💡 Candidates should trace how nested function executions create stacked context layers.

Question #54: What are WeakMap and WeakSet, and how do they prevent memory leaks compared to Map and Set?

Level: Mid-Level | Category: Memory Management | Time: 7 mins

Expected Answer

WeakMap and WeakSet differ in how they hold references to keys and values, which prevents memory leaks:

  • WeakMap: Keys must be objects or non-registered symbols. The references to the keys are held **weakly**. If a key object has no other active references, it can be garbage-collected, and its value will be removed from the WeakMap automatically.
  • WeakSet: Similar behavior, storing unique objects weakly.
  • Memory Leaks: Standard Maps hold strong references. If you delete a key reference from your variables but forget to clear it from the Map, the object stays in memory, creating a memory leak.

Code Example


let user = { name: 'John' };
const wm = new WeakMap();
wm.set(user, 'profile info');

user = null; // The object is now eligible for Garbage Collection!
// In a standard Map, the object would stay in memory until the Map is cleared.
Interviewer Note: 💡 Ask about limitations: WeakMaps cannot be iterated over, and you cannot read their size properties.

Question #55: How does AbortController work in JavaScript, and how do you use it to cancel Fetch requests?

Level: Mid-Level | Category: Async Programming | Time: 8 mins

Expected Answer

AbortController is a built-in browser API that allows you to abort one or more Web requests dynamically.

  • Controller: Instantiate new AbortController() to get an object containing an abort() method and a signal property.
  • Signal Binding: Pass the signal reference as an option to your fetch() request.
  • Trigger: Calling controller.abort() cancels the request, causing the fetch promise to reject with an AbortError.

Code Example


const controller = new AbortController();
const { signal } = controller;

fetch('/api/heavy-data', { signal })
  .then(res => res.json())
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Fetch request was successfully aborted.');
    }
  });

// Abort the request after 2 seconds
setTimeout(() => controller.abort(), 2000);
Interviewer Note: 💡 Important for performance: ask them how to use it to prevent race conditions in search autocomplete inputs.

Question #56: Explain JavaScript Generators (function*) and Iterators. What are their use cases?

Level: Mid-Level | Category: Async Programming | Time: 8 mins

Expected Answer

Generators and Iterators enable custom traversal of data structures:

  • Iterator: An object containing a next() method that returns `{ value, done }`.
  • Generator: A function declared using function* that returns a generator iterator. It uses the yield keyword to pause execution and emit values lazily.
  • Lazy Evaluation: They only compute values when requested, making them ideal for handling infinite data streams or reading large files line by line without holding them in memory.

Code Example


function* numberGenerator() {
  let i = 0;
  while (true) {
    yield i++;
  }
}

const gen = numberGenerator();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
Interviewer Note: 💡 A mid-level JS check. Verify they know how generators yield execution control back to caller scopes.

Question #57: What are Symbols, and why are they used to create private-like properties or well-known protocols (e.g. Symbol.iterator)?

Level: Mid-Level | Category: Modern Syntax | Time: 7 mins

Expected Answer

A Symbol is a unique primitive value introduced in ES6.

  • Guaranteed Uniqueness: Every Symbol() call returns a unique value, preventing key collisions in objects.
  • Hidden Properties: Symbol properties do not appear in Object.keys() or for...in loops. They are only readable using Object.getOwnPropertySymbols().
  • Well-Known Symbols: JavaScript engines use built-in symbols like Symbol.iterator or Symbol.toStringTag to define core object protocols.

Code Example


const privateKey = Symbol('id');
const user = {
  [privateKey]: 'secret-101',
  name: 'Alice'
};
console.log(Object.keys(user)); // ['name']
console.log(user[privateKey]); // 'secret-101'
Interviewer Note: 💡 Ask: 'Can Symbols be parsed from JSON?' (Answer: No, JSON.stringify skips Symbol properties).

Question #58: How does the V8 Garbage Collector work (Generational GC, Scavenger, Mark-Sweep-Compact)?

Level: Mid-Level | Category: Garbage Collection | Time: 8 mins

Expected Answer

The V8 Engine uses a generational garbage collector to reclaim unused heap memory. Memory is split into two generations:

  • Young Generation (New Space): Small space where new objects are created. Collected frequently using the Scavenger algorithm (Cheney's copying algorithm), which splits space in half and copies surviving objects over. Surviving objects are eventually promoted to Old Space.
  • Old Generation (Old Space): Large space where surviving objects reside. Collected less frequently using the Mark-Sweep-Compact algorithm: Marks reachable references, Sweeps unmarked objects, and Compacts fragmented memory slots.
Interviewer Note: 💡 Candidates should understand the 'Weak Generational Hypothesis': most objects die young, which is why V8 separates memory spaces.

Question #59: Explain the difference between debounce and throttle. Write a custom implementation of a debounce function.

Level: Mid-Level | Category: Functional Programming | Time: 8 mins

Expected Answer

Both limit execution rate, but behave differently:

  • Debounce: Delays execution until events have stopped firing for a set time (e.g. autocomplete input).
  • Throttle: Restricts execution to once per time window (e.g. scroll tracking, resizing).

Code Example


function debounce(fn, delay) {
  let timerId = null;
  return function (...args) {
    if (timerId) clearTimeout(timerId);
    timerId = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}
Interviewer Note: 💡 Check if the candidate handles 'this' context and parameters inside the debounced returned function.

Question #60: Explain currying in functional programming. Write a function that curries a 3-argument function.

Level: Mid-Level | Category: Functional Programming | Time: 7 mins

Expected Answer

Currying transforms a function that takes multiple arguments into a chain of nested functions that each take a single argument: fn(a, b, c) becomes fn(a)(b)(c). This enables partial function application, which helps build re-usable configuration functions.

Code Example


// Currying helper
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    return function(...args2) {
      return curried.apply(this, args.concat(args2));
    };
  };
}

// Usage
const sum = (a, b, c) => a + b + c;
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 6
Interviewer Note: 💡 Candidates should write the curry helper using recursion, comparing argument lengths to determine when to execute the target function.

Question #61: Explain the Critical Rendering Path in browsers (HTML -> DOM, CSS -> CSSOM -> Render Tree -> Layout -> Paint -> Composite).

Level: Mid-Level | Category: Browser Rendering | Time: 8 mins

Expected Answer

The Critical Rendering Path (CRP) is the sequence of steps the browser takes to convert HTML, CSS, and JS into visible pixels on the screen:

  • DOM: Browser parses HTML and builds the Document Object Model tree.
  • CSSOM: Browser parses CSS and builds the CSS Object Model tree.
  • Render Tree: DOM and CSSOM are combined, filtering out invisible elements (e.g. display: none).
  • Layout (Reflow): Calculates the geometry, position, and size of elements.
  • Paint: Fills in pixels (colors, borders, text styles) into layers.
  • Composite: Sends layers to the GPU to be composited and drawn onto the screen.
Interviewer Note: 💡 Ask the candidate how script tags impact DOM parsing (blocking unless async or defer attributes are set).

Question #62: What are Web Workers, and when should you use them to offload CPU-bound tasks?

Level: Mid-Level | Category: Performance Optimization | Time: 8 mins

Expected Answer

Web Workers allow running scripts in background threads, isolated from the browser's main thread.

  • No UI Access: Workers cannot access the DOM or window properties directly. They communicate with the main thread using postMessage() and onmessage.
  • Use Cases: Performing CPU-intensive tasks (e.g. data processing, image filtering, parsing large files) without blocking the UI main thread.

Code Example


// main.js
const worker = new Worker('worker.js');
worker.postMessage({ number: 40 });
worker.onmessage = (e) => {
  console.log('Result from worker:', e.data.result);
};

// worker.js
self.onmessage = (e) => {
  const result = fibonacci(e.data.number);
  self.postMessage({ result });
};
Interviewer Note: 💡 Verify they understand that Web Workers use message serialization (structured clone algorithm) to share data across threads.

Question #63: What is the difference between Object.freeze(), Object.seal(), and Object.preventExtensions()?

Level: Mid-Level | Category: Objects | Time: 7 mins

Expected Answer

These methods restrict modifications to objects at different levels:

  • Object.preventExtensions(): Prevents adding new properties. Existing properties can be modified or deleted.
  • Object.seal(): Prevents adding or deleting properties. Sets all properties as non-configurable, but existing property values can still be updated.
  • Object.freeze(): Prevents adding, deleting, or updating properties. Makes all properties read-only. This creates a shallow freeze (nested objects can still be mutated).
Interviewer Note: 💡 Check if the candidate knows these restrictions only throw errors in Strict Mode; in non-strict mode, they fail silently.

Question #64: What are the JavaScript Proxy and Reflect APIs? How do they enable reactivity (like Vue 3)?

Level: Senior | Category: Advanced Concepts | Time: 12 mins

Expected Answer

The Proxy and Reflect APIs work together to intercept and customize operations on objects:

  • Proxy: Wraps a target object and intercepts operations (like property access, assignment, function calls) using handler traps (e.g. get, set).
  • Reflect: A global object containing methods that match Proxy traps, which forwards intercepted operations to the target object.
  • Reactivity: In reactive frameworks (like Vue 3), a Proxy intercepts property sets. When a property is updated, the Proxy triggers change notifications and UI re-renders, while Reflect updates the actual object state.

Code Example


const target = { count: 0 };
const proxy = new Proxy(target, {
  get(target, prop, receiver) {
    console.log(`Accessing property: ${prop}`);
    return Reflect.get(target, prop, receiver);
  },
  set(target, prop, value, receiver) {
    console.log(`Setting property: ${prop} = ${value}`);
    // Trigger side-effects here (e.g. UI re-renders)
    return Reflect.set(target, prop, value, receiver);
  }
});
proxy.count = 5; // logs 'Setting property: count = 5'
Interviewer Note: 💡 Look for the candidate's understanding of Receiver arguments: they ensure correct inheritance when calling getters/setters on inherited objects.

Question #65: Detail the inner workings of the V8 Engine compiler pipeline (Ignition parser, Sparkplug, Maglev, TurboFan optimizer). What are inline caches and hidden classes?

Level: Senior | Category: V8 Engine | Time: 13 mins

V8 Pipeline

  1. Parser: Converts JavaScript source code into an Abstract Syntax Tree (AST).
  2. Ignition Interpreter: Generates bytecode from the AST and starts running it quickly.
  3. Sparkplug & Maglev: Fast, mid-level compilers that generate non-optimized machine code to speed up hot execution paths.
  4. TurboFan Optimizer: The optimizing compiler. It takes hot functions and builds highly optimized machine code using execution feedback. If assumptions fail (e.g. types change), it de-optimizes back to Ignition bytecode.

V8 Optimizations

  • Hidden Classes (Shapes): JavaScript lacks static typing, but V8 creates internal 'hidden classes' (shapes) to track object properties. If objects share the same properties in the same order, they share a shape, allowing V8 to optimize property accesses.
  • Inline Caches (IC): V8 caches property offsets directly in function calls. If objects share the same shape, property lookups bypass slow dictionary hash tables, executing instantly.
Interviewer Note: 💡 This is an advanced compiler query. Ask the candidate why adding properties dynamically at runtime (causing polymorphic shapes) slows down execution.

Question #66: Explain the difference between microtasks and macrotasks in the Event Loop. What happens to rendering updates in relation to these queues?

Level: Senior | Category: Event Loop | Time: 11 mins

Expected Answer

The Event Loop processes asynchronous tasks sequentially:

  • Macrotasks: Scheduled tasks like setTimeout, setInterval, postMessage, and standard DOM events. They run one at a time.
  • Microtasks: High-priority tasks like Promise.then(), queueMicrotask, and MutationObserver. The microtask queue is **flushed completely** after the current execution context, before returning control to the Event Loop.
  • Rendering: Browser layout and repaint cycles happen after all microtasks have run, before processing the next macrotask.

Execution Flow

1. Execute a macrotask (e.g., initial script).
2. Run all microtasks until the queue is empty.
3. Check if rendering is needed and repaint if necessary.
4. Fetch the next macrotask from the queue.

Interviewer Note: 💡 Verify they understand that an infinite loop of microtasks (e.g., recursive Promise loops) starves the event loop, freezing the browser and blocking UI rendering.

Question #67: How do you implement a robust Custom Promise implementation from scratch?

Level: Senior | Category: Async Programming | Time: 12 mins

Expected Solution

Build a Promise class implementing states (PENDING, FULFILLED, REJECTED) and managing handlers dynamically. It must execute handlers asynchronously (using microtasks) to comply with standard specs.

Code Example


class MyPromise {
  constructor(executor) {
    this.state = 'PENDING';
    this.value = null;
    this.handlers = [];

    const resolve = (val) => {
      if (this.state !== 'PENDING') return;
      this.state = 'FULFILLED';
      this.value = val;
      this.handlers.forEach(h => h.onFulfilled(val));
    };

    const reject = (err) => {
      if (this.state !== 'PENDING') return;
      this.state = 'REJECTED';
      this.value = err;
      this.handlers.forEach(h => h.onRejected(err));
    };

    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
}
Interviewer Note: 💡 Look for correct handler queuing when then() is called after the promise has already resolved.

Question #68: What are the new ES2024/ES2025 features (e.g., Promise.withResolvers, Iterator Helpers, Object.groupBy, Temporal API)?

Level: Senior | Category: Modern ES Features | Time: 10 mins

Expected Answer

Modern ECMAScript specifications introduce features that simplify common patterns:

  • Promise.withResolvers(): Returns a Promise along with its resolve and reject functions, which avoids nesting resolve logic inside executor callbacks.
  • Object.groupBy() / Map.groupBy(): Groups iterable elements by criteria keys.
  • Iterator Helpers: Adds utility methods (like map, filter, take, drop) directly to JS generators and iterators.
  • Temporal API: A modern date/time library replacing the buggy legacy Date object.

Code Example


// Promise.withResolvers
const { promise, resolve, reject } = Promise.withResolvers();
setTimeout(() => resolve('done'), 1000);

// Object.groupBy
const list = [{ role: 'admin' }, { role: 'user' }];
const grouped = Object.groupBy(list, x => x.role);
Interviewer Note: 💡 Tests candidate's commitment to keeping up with modern standards. Ask about browser support and polyfill strategies.

Question #69: How do you secure JavaScript applications against Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF)?

Level: Senior | Category: Security | Time: 11 mins

Expected Answer

Security requires multi-layered defensive validation:

  • XSS Mitigation: Never insert untrusted strings directly into HTML rendering methods (e.g. element.innerHTML). Sanitize inputs using libraries like DOMPurify and configure strict Content Security Policies (CSP).
  • CSRF Mitigation: Store authentication tokens in HTTP-only, secure cookies with the SameSite=Strict attribute. Attach anti-CSRF tokens to API requests and verify them on the server.
Interviewer Note: 💡 Ask: 'Why is LocalStorage unsafe for token storage?' (Answer: It is accessible by any script running on the page, making tokens vulnerable to theft via XSS).

Question #70: Explain Memoization. Write a generic memoize function that handles multiple arguments and custom hash keys.

Level: Senior | Category: Functional Programming | Time: 11 mins

Expected Answer

Memoization is an optimization technique that caches function results based on input parameters. If a function is called with cached inputs, it returns the result instantly without re-calculating.

Code Example


function memoize(fn, resolver) {
  const cache = new Map();
  return function (...args) {
    const key = resolver ? resolver(...args) : JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}
Interviewer Note: 💡 A good candidate discusses memory management, noting that unbounded memoization caches can lead to memory leaks, which can be mitigated by using cache eviction strategies (e.g. LRU cache).

Question #71: How does V8 optimize Array storage (Smi elements, Double elements, Dictionary elements)? How do holes in arrays impact performance?

Level: Senior | Category: V8 Engine | Time: 12 mins

Expected Answer

V8 optimizes arrays dynamically based on their elements' type:

  • Smi Elements: Small Integers. Stored as contiguous arrays of unboxed integers (fastest).
  • Double Elements: Floating-point numbers. Stored as unboxed floating-point arrays.
  • Dictionary Elements: Stored as slow hash tables. This occurs when arrays are sparse or contain mixed types (e.g. objects, strings).
  • Holes (Sparse Arrays): Deletes elements or leaves holes (e.g. [1,,3]). This de-optimizes arrays, forcing V8 to check prototype chains for missing values, which degrades performance.
Interviewer Note: 💡 Check if the candidate knows to avoid creating sparse arrays or changing array element types dynamically to preserve compiler optimizations.

Question #72: What is the difference between Reflow (Layout) and Repaint in the browser DOM? How do you write code to avoid layout thrashing?

Level: Senior | Category: Performance Optimization | Time: 12 mins

Expected Answer

These updates represent different stages of browser rendering:

  • Reflow (Layout): Recalculates the geometry and layout of elements. Changing properties like width, height, top, or margin forces a reflow, which is expensive because it affects parent and sibling elements.
  • Repaint: Redraws elements without changing their layout. Triggered by changes to properties like color, visibility, or background-color. Faster than a reflow.
  • Layout Thrashing: Occurs when you repeatedly read and write to the DOM in rapid succession (e.g. inside loops). This forces the browser to recalculate layouts synchronously on every iteration.

Best Practices

  • Batch DOM reads and writes: read properties first, then apply all style updates.
  • Use DocumentFragments or wrap DOM mutations in requestAnimationFrame().
  • Use GPU-accelerated CSS properties (like transform and opacity) to bypass reflows and repaints.
Interviewer Note: 💡 Verify they understand how layout thrashing degrades frame rates and how to debug it using the Chrome Performance tab.

Question #73: Explain the Module Pattern, Revealing Module Pattern, and how they relate to closures and modern block scopes.

Level: Senior | Category: Design Patterns | Time: 11 mins

Expected Answer

Before native ES Modules, closures and IIFEs were used to manage scoping:

  • Module Pattern: Encapsulates private state and behavior inside an IIFE, returning a public API object.
  • Revealing Module Pattern: Similar structure, but declares all functions and variables locally in the closure, returning an object with pointers to private members. This keeps syntax clean.
  • Modern Alternative: ES Modules with const/let block scoping replace IIFE module patterns, resolving dependency imports natively at compile time.

Code Example


const CounterModule = (function () {
  let privateCount = 0; // Private state
  
  function increment() { privateCount++; }
  function getCount() { return privateCount; }
  
  return {
    increment,
    count: getCount // Reveal public pointers
  };
})();
Interviewer Note: 💡 Ask about advantages: private encapsulation blocks outside tampering, keeping state secure.

Question #74: Write a deep clone function that handles circular references, Dates, RegExps, Maps, and Sets.

Level: Senior | Category: Functional Programming | Time: 12 mins

Expected Answer

A complete deep copy implementation must handle non-JSON types (like Date, Map, RegExp) and prevent infinite recursion by tracking visited objects using a WeakMap cache.

Code Example


function deepClone(obj, hash = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  
  if (hash.has(obj)) return hash.get(obj); // Resolve circular ref
  
  const clone = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
  hash.set(obj, clone);
  
  // Handle Map and Set
  if (obj instanceof Map) {
    obj.forEach((val, key) => clone.set(deepClone(key, hash), deepClone(val, hash)));
    return clone;
  }
  if (obj instanceof Set) {
    obj.forEach(val => clone.add(deepClone(val, hash)));
    return clone;
  }
  
  Reflect.ownKeys(obj).forEach(key => {
    clone[key] = deepClone(obj[key], hash);
  });
  return clone;
}
Interviewer Note: 💡 Evaluate their handling of circular references (without the WeakMap cache, circular references will cause stack overflow errors).

Question #75: Explain the difference between Node.js thread pool (libuv) and browser Web APIs. How do their event loops differ?

Level: Senior | Category: Event Loop | Time: 11 mins

Expected Answer

The differences lie in host environments and runtime targets:

  • Browser Web APIs: Handle DOM events, timers, and animations. The event loop coordinates tasks with rendering repaints.
  • Node.js (Libuv): A C++ library providing asynchronous I/O (files, networking) and managing a thread pool (4 threads by default) for heavy blocking tasks.
  • Event Loop Phases: Node.js has specific execution phases (timers, pending callbacks, poll, check, close callbacks), whereas the browser uses simple macrotask/microtask queues.
Interviewer Note: 💡 Ask the candidate about process.nextTick() in Node.js. (Answer: NextTick executes immediately after the current operation, running before promise microtasks).

Architect Question #6: How do you design and architect a scalable, type-safe client-side event bus for a micro-frontend shell application?

Level: Architect | Category: System Design | Time: 15 mins

Expected Answer

A micro-frontend shell requires a decoupled, secure event bus for cross-application communication:

  • Decoupling: Apps should communicate strictly via event messages, avoiding direct dependencies or shared variables.
  • Type-Safety: Use TypeScript interfaces to enforce event payload contracts, preventing applications from sending invalid data shapes.
  • Memory Management: Event handlers must be unregistered when micro-frontends unmount to prevent memory leaks.

Architecture Blueprint


class EventBus {
  private listeners = new Map();

  publish(event, payload) {
    const handlers = this.listeners.get(event);
    if (handlers) {
      handlers.forEach(fn => fn(payload));
    }
  }

  subscribe(event, callback) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
    }
    this.listeners.get(event).add(callback);
    return () => this.listeners.get(event).delete(callback); // Return unsubscribe fn
  }
}
Interviewer Note: 💡 Look for the unsubscribe return pattern, which enables simple, clean cleanups in component destruction lifecycle hooks.

Architect Question #7: How do SOLID principles apply to dynamic, prototype-based JavaScript development?

Level: Architect | Category: Design Patterns | Time: 15 mins

Expected Answer

While SOLID was created for statically typed languages, its principles are highly relevant to JavaScript development:

  • Single Responsibility: Modules should export single functions or classes focused on a single task, avoiding bloated utility scripts.
  • Open/Closed: Extend component behaviors using functional delegation or composition instead of modifying original constructor definitions.
  • Liskov Substitution: Ensure subclasses respect the parent class's contract, avoiding overriding methods with incompatible argument signatures.
  • Interface Segregation: JS lacks native interfaces, but we can enforce contracts using duck typing or parameter validation checks.
  • Dependency Inversion: Inject dependencies as parameters or use DI containers rather than importing concrete service classes directly.
Interviewer Note: 💡 Strong candidates explain why composition is preferred over inheritance (Prototype Chain) in JavaScript.

Architect Question #8: Design an offline-first caching and data synchronization architecture using Service Workers, Cache API, and IndexedDB.

Level: Architect | Category: System Design | Time: 15 mins

Expected Answer

An offline-first architecture uses service workers and local databases to handle network connectivity shifts:

  • Service Worker: Intercepts network fetches. Uses caching strategies like Stale-While-Revalidate or Cache-First for static assets.
  • IndexedDB: Stores structured application data locally (e.g. drafts, logs) because LocalStorage is synchronous and size-limited.
  • Sync Engine: Uses the browser's Background Sync API to queue actions while offline, sending them to the server automatically when connection is restored.
Interviewer Note: 💡 Check if the candidate discusses conflict resolution strategies (e.g. Last-Write-Wins, client-server reconciliation) when syncing offline modifications.

Architect Question #9: Design a performance monitoring library that tracks Core Web Vitals (LCP, FID, CLS, INP) and sends beacon data.

Level: Architect | Category: System Design | Time: 15 mins

Expected Answer

A performance library must capture browser metric shifts without impacting page loading performance:

  • PerformanceObserver: Use this API to listen to performance events (like paint events, layout shifts, user interactions) asynchronously.
  • Beacon API: Send collected data using navigator.sendBeacon(). This sends data asynchronously in the background when the user closes the page, preventing request cancellation.

Code Example


// Listen for cumulative layout shifts (CLS)
let clsScore = 0;
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    if (!entry.hadRecentInput) {
      clsScore += entry.value;
    }
  }
}).observe({ type: 'layout-shift', buffered: true });

// Send results on page exit
window.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    navigator.sendBeacon('/api/analytics', JSON.stringify({ cls: clsScore }));
  }
});
Interviewer Note: 💡 Verify they understand why visibilitychange is preferred over beforeunload for sending final analytics data.

Architect Question #10: How would you build a custom compiler/parser in JavaScript (Lexer, Parser, AST, Evaluator)? Explain AST representations.

Level: Architect | Category: Compiler Design | Time: 15 mins

Expected Answer

Building a compiler requires three main pipeline steps:

  • Lexical Analyzer (Lexer): Scans the source code string and breaks it down into a list of syntax tokens (e.g. keyword, identifier, operator).
  • Syntactic Analyzer (Parser): Processes tokens sequentially and builds an Abstract Syntax Tree (AST) representing the code structure.
  • Evaluator (Interpreter): Recursively traverses the AST nodes to execute code instructions, or generates target assembly/JS bytecode.
Interviewer Note: 💡 Look for candidate's knowledge of tree traversal algorithms (Recursive Descent parsing) to build AST representations.

Architect Question #11: Architect a secure JWT-based authentication flow with silent token refresh, CSRF mitigation, and secure cookie storage.

Level: Architect | Category: Security | Time: 15 mins

Expected Answer

A secure authentication flow requires splitting token responsibilities:

  • Access Token: Short-lived JWT (e.g. 15 minutes). Stored in memory (JavaScript variable) to prevent XSS theft. Passed in the Authorization: Bearer header.
  • Refresh Token: Long-lived token. Stored in an HttpOnly, Secure, SameSite=Strict cookie, making it inaccessible to client-side scripts (XSS proof).
  • Silent Refresh: When the access token is about to expire, the client sends a background request to the token endpoint. The browser automatically appends the refresh cookie, generating a new access token without requiring user interaction.
Interviewer Note: 💡 Ask about CSRF mitigation for the refresh token cookie (SameSite validation and double-submit anti-CSRF token verification).

Architect Question #12: How would you optimize the performance of a high-frequency real-time canvas chart that renders 60fps data streams?

Level: Architect | Category: System Design | Time: 15 mins

Expected Answer

60fps rendering requires drawing a new frame every 16.6ms. To prevent rendering lag:

  • OffscreenCanvas: Render charts on an OffscreenCanvas inside a Web Worker, freeing up the main thread to handle user interactions.
  • Avoid Object Allocation: Avoid creating new object instances (e.g. coordinate objects, arrays) in the rendering loop to prevent garbage collection pauses. Use pre-allocated, flat TypedArrays instead.
  • RequestAnimationFrame: Coordinate main-thread drawings with the browser's refresh rate using requestAnimationFrame().
Interviewer Note: 💡 Look for candidate's strategies to minimize garbage collection runs inside the rendering loop.

Architect Question #13: Design a micro-dependency-injection container for a vanilla JavaScript framework.

Level: Architect | Category: Design Patterns | Time: 15 mins

Expected Answer

A DI container resolves class dependencies recursively based on registered token maps:

  • Registry: A map storing provider tokens linked to factory functions or class definitions.
  • Resolution: A method that parses class constructor parameters (using reflection metadata or parameter parsing) and instantiates them recursively, caching singleton instances.

Code Example


class Injector {
  private registry = new Map();
  private singletons = new Map();

  register(token, klass) {
    this.registry.set(token, klass);
  }

  resolve(token) {
    if (this.singletons.has(token)) return this.singletons.get(token);
    const klass = this.registry.get(token);
    // Determine constructor dependencies recursively
    const instance = new klass(); 
    this.singletons.set(token, instance);
    return instance;
  }
}
Interviewer Note: 💡 Ask the candidate how they would detect circular dependencies (e.g. keeping track of resolved tokens in a stack during instantiation).

Angular Trap #17: Why does [1, 2, 10].sort() result in [1, 10, 2]? Explain default sort behavior and custom sorting.

Level: Tricky Question | Category: Data Structures | Time: 10 mins

Expected Answer

By default, Array.prototype.sort() converts elements to strings and compares their UTF-16 code unit values. Because "10" starts with "1", it is sorted before "2" alphabetically.

Solution: Pass a custom comparison function: arr.sort((a, b) => a - b).

Interviewer Note: 💡 Check if the candidate knows that sort() mutates the original array in place.

Angular Trap #18: What is the difference between __proto__ and prototype in JavaScript? Why is modifying __proto__ a major performance hazard in V8?

Level: Tricky Question | Category: V8 Engine | Time: 10 mins

Expected Answer

These properties serve different roles in prototype inheritance:

  • prototype: A property on constructor functions used to define methods shared by all instances created with new.
  • __proto__: A getter/setter accessor on objects pointing to their active prototype instance.
  • V8 De-optimization: Modifying __proto__ at runtime alters an object's prototype chain dynamically. This invalidates all optimized machine code and shapes (hidden classes) cached by V8, forcing execution back to the slow bytecode interpreter.
Interviewer Note: 💡 Use Object.create() to configure prototypes at creation time instead of modifying __proto__ at runtime.

Angular Trap #19: Why does typeof NaN return 'number', and why is NaN === NaN false? How do you safely check for NaN?

Level: Tricky Question | Category: Modern Syntax | Time: 10 mins

Expected Answer

In ECMAScript, NaN (Not-a-Number) represents invalid numerical calculations, but its primitive type is still numeric. According to IEEE 754 floating-point standards, NaN compares unequally to all values, including itself.

Solution: Use Number.isNaN(val), which verifies if the value is NaN without coercion. Avoid global isNaN() because it coerces inputs to numbers, producing false positives (e.g. isNaN('hello') is true).

Interviewer Note: 💡 Ask the candidate how Object.is() handles NaN. (Answer: Object.is(NaN, NaN) returns true).

Angular Trap #20: Why does 0.1 + 0.2 === 0.3 return false? Explain IEEE 754 double-precision floating-point format and safe calculations.

Level: Tricky Question | Category: Fundamentals | Time: 10 mins

Expected Answer

JavaScript numbers are stored as 64-bit floating-point values according to IEEE 754 standards. Values like 0.1 and 0.2 cannot be represented exactly in binary fractional notation, which leads to rounding errors (e.g. 0.1 + 0.2 yields 0.30000000000000004).

Solutions: Compare differences against a small tolerance value: Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON, or multiply values to integers before calculations. Use libraries like decimal.js for financial operations.

Interviewer Note: 💡 Essential for financial applications. Candidates must know that precision limits affect calculations.

Angular Trap #21: Why does a closure over a variable inside a loop using let create a new binding for each iteration, whereas var binds to the same reference?

Level: Tricky Question | Category: Scope & Hoisting | Time: 10 mins

Expected Answer

These variables handle scope and binding differently inside loops:

  • var: Function-scoped. The loop shares a single variable binding in memory. All closures created inside the loop point to this single reference, which holds the final loop value.
  • let: Block-scoped. The engine creates a new variable binding for each loop iteration, copying the previous value over. Each closure captures a unique block scope containing that iteration's value.
Interviewer Note: 💡 A classic scope question. Look for a clear distinction between block scope and function scope.

Angular Trap #22: Why does Array.prototype.map combined with parseInt (['1', '2', '3'].map(parseInt)) return [1, NaN, NaN]?

Level: Tricky Question | Category: Functions | Time: 10 mins

Expected Answer

This unexpected result is caused by argument mismatches between the two functions:

  • map() passes three arguments to its callback: (value, index, array).
  • parseInt() accepts two arguments: (string, radix).
  • During execution, map passes indices to parseInt as radix arguments:
    - Index 0: parseInt('1', 0) → Radix 0 defaults to base 10 (returns 1).
    - Index 1: parseInt('2', 1) → Radix 1 is invalid (returns NaN).
    - Index 2: parseInt('3', 2) → '3' is invalid in binary (returns NaN).

Fix: Explicitly pass arguments: arr.map(num => parseInt(num, 10)).

Interviewer Note: 💡 Demonstrates why you should avoid passing functions directly to map callbacks if they accept optional arguments.

Angular Trap #23: What is the temporal dead zone (TDZ)? Why does typeof undeclared return 'undefined', but typeof declaredWithLet throws an error?

Level: Tricky Question | Category: Scope & Hoisting | Time: 10 mins

Expected Answer

The Temporal Dead Zone (TDZ) is the period from the start of a block scope until the variable is initialized.

  • Undeclared Variables: Resolves dynamically. Since the engine has no reference to it, typeof safely returns "undefined".
  • Declared with Let/Const: The compiler registers the variable but places it in the TDZ. Accessing it (even via typeof) before its initialization throws a ReferenceError.
Interviewer Note: 💡 Check if the candidate knows that let/const declarations are hoisted but not initialized.

Angular Trap #24: Why does JSON.stringify strip functions, Symbols, and undefined values from objects? How do you serialize them?

Level: Tricky Question | Category: Objects | Time: 10 mins

Expected Answer

The JSON standard only supports basic data types (strings, numbers, booleans, objects, arrays, and null). Because functions, symbols, and undefined values are not part of the JSON specification, JSON.stringify() strips them from object properties (or converts them to null inside arrays).

Serialization: Pass a custom replacer function as the second argument to JSON.stringify() to format these values manually. For example, convert functions to string templates.

Interviewer Note: 💡 Ask about the toJSON() method on objects, which override default stringify behavior.

Production Incident #10: Browser Memory Leak due to detached DOM trees bound to Event Listeners inside dynamic views

Level: Production Incident | Category: Garbage Collection | Time: 15 mins

Scenario

A single-page application experiences slow performance over time. Heap snapshots show that DOM nodes deleted from the page are still retained in memory.

Root Cause Analysis

Detached DOM nodes occur when an HTML element is removed from the DOM tree, but a JavaScript reference (like an active event listener or global variable) still points to it. Because of this reference, the garbage collector cannot reclaim the memory, leading to a memory leak.

Resolution

  • Remove event listeners using removeEventListener before removing elements from the DOM.
  • Use weak references (WeakRef or WeakMap) when binding metadata to DOM elements.
Interviewer Note: 💡 Check if the candidate knows how to locate detached DOM nodes using the Chrome DevTools Memory panel.

Production Incident #11: Race condition in autocomplete search input returning outdated user search results

Level: Production Incident | Category: Async Programming | Time: 15 mins

Scenario

Users typing in a search bar report that the results shown sometimes match older search queries rather than their final query.

Root Cause Analysis

API requests resolve at different times depending on network latency. If an earlier request (e.g. search for 'abc') takes longer than a subsequent request (e.g. search for 'abcd'), the first request will resolve last, overwriting the UI with outdated results.

Resolution

  • Cancel pending requests when triggering new ones using AbortController.
  • If using RxJS, use the switchMap operator to automatically cancel previous request streams.
Interviewer Note: 💡 A common async bug. Candidates should discuss cancellation patterns rather than just debouncing inputs.

Production Incident #12: Browser UI freezes due to heavy JSON parsing block on the Main Thread

Level: Production Incident | Category: Performance Optimization | Time: 15 mins

Scenario

A dashboard application freezes for several seconds when loading heavy reports, making the page unresponsive to clicks.

Root Cause Analysis

JSON.parse() is synchronous and runs on the browser's main thread. Parsing massive JSON payloads (e.g. 50MB reports) blocks the main thread, stopping the event loop and freezing user interactions.

Resolution

  • Offload parsing to a **Web Worker**, resolving the parsed objects back to the main thread via message channels.
  • Use streaming JSON parsers to parse chunks of data progressively.
Interviewer Note: 💡 Check if the candidate understands main-thread blocking behaviors and how Web Workers isolate executions.

Production Incident #13: Web application crashes on iOS Safari due to memory exhaustion when loading large image blobs

Level: Production Incident | Category: Performance Optimization | Time: 15 mins

Scenario

An image-sharing portal frequently crashes on mobile browsers (especially Safari on iOS) when loading high-resolution images.

Root Cause Analysis

Creating image object URLs using URL.createObjectURL(blob) caches the blob data in memory. Unlike standard variables, these URL references are not garbage-collected automatically when DOM elements are removed. If they are not revoked, memory usage accumulates, crashing mobile browsers with strict memory limits.

Resolution

  • Revoke image URLs using URL.revokeObjectURL(url) once the image element has finished loading.
  • Convert blobs to Base64 data URLs or use streams to minimize memory usage.
Interviewer Note: 💡 Tests mobile performance awareness. Candidates must know that memory resources are highly constrained on mobile platforms.

Production Incident #14: Session tokens exposed via XSS due to localstorage token storage

Level: Production Incident | Category: Security | Time: 15 mins

Scenario

A security audit reveals that a malicious script injected via an XSS exploit was able to steal user session tokens and hijack accounts.

Root Cause Analysis

The application stored JWT session tokens in LocalStorage. Because LocalStorage is accessible by any script running on the page, the XSS exploit was able to read the token using localStorage.getItem('token') and send it to an attacker's server.

Resolution

  • Store session tokens in secure, HttpOnly cookies to prevent access via JavaScript.
  • If cookies cannot be used, store tokens in memory (JavaScript state variables) and use silent refresh endpoints to fetch new tokens.
Interviewer Note: 💡 Candidates should talk about security trade-offs between local storage and cookies.

Production Incident #15: Infinite loop in recursive deep clone function caused by circular references

Level: Production Incident | Category: Functional Programming | Time: 15 mins

Scenario

A data visualization app freezes and crashes with a RangeError: Maximum call stack size exceeded when copying complex data models.

Root Cause Analysis

The deep copy function recursively copies nested properties. If an object contains a circular reference (pointing back to itself or a parent element), the function recurses indefinitely, exhausting the call stack.

Resolution

  • Track visited objects using a WeakMap cache. If an object has already been copied, return the cached clone instead of recursing again.
  • Use built-in alternatives like structuredClone(), which handle circular references automatically.
Interviewer Note: 💡 Check if the candidate knows how recursive calls exhaust stack limits and how caches prevent infinite loops.

Coding Challenge #7: Implement a throttled function with leading and trailing execution options.

Level: Coding Challenge | Category: Functional Programming | Time: 20 mins

Expected Solution

Write a throttle function that restricts execution rate, support options for leading (run at the start of the window) and trailing (run at the end of the window) executions.

Code Example


function throttle(fn, wait, options = {}) {
  let timerId = null;
  let lastArgs = null;
  let lastThis = null;
  let lastCallTime = 0;

  const invoke = () => {
    fn.apply(lastThis, lastArgs);
    lastCallTime = Date.now();
    timerId = null;
  };

  return function (...args) {
    lastArgs = args;
    lastThis = this;
    const now = Date.now();
    const remaining = wait - (now - lastCallTime);

    if (remaining <= 0) {
      if (timerId) {
        clearTimeout(timerId);
        timerId = null;
      }
      if (options.leading !== false) {
        invoke();
      }
    } else if (!timerId && options.trailing !== false) {
      timerId = setTimeout(invoke, remaining);
    }
  };
}
Interviewer Note: 💡 An advanced functional programming challenge. Pay close attention to timing, parameters passing, and context binding.

Coding Challenge #8: Write an asynchronous task runner that limits concurrency to N tasks.

Level: Coding Challenge | Category: Async Programming | Time: 20 mins

Expected Solution

Implement a queue manager that accepts an array of asynchronous task factories and executes them, ensuring that no more than N tasks run concurrently.

Code Example


async function limitConcurrency(tasks, limit) {
  const results = [];
  const executing = new Set();

  for (const [index, task] of tasks.entries()) {
    const p = Promise.resolve().then(() => task()).then(res => {
      results[index] = res;
      executing.delete(p);
    });
    executing.add(p);
    
    if (executing.size >= limit) {
      await Promise.race(executing); // Wait for any task to finish
    }
  }
  await Promise.all(executing); // Wait for remaining tasks
  return results;
}
Interviewer Note: 💡 Highly relevant for performance. Shows how to avoid overloading APIs when sending concurrent fetch requests.

Coding Challenge #9: Implement a simple EventEmitter class from scratch.

Level: Coding Challenge | Category: Design Patterns | Time: 20 mins

Expected Solution

Create a pub/sub event dispatcher class supporting on(), off(), once(), and emit() methods.

Code Example


class EventEmitter {
  constructor() {
    this.events = new Map();
  }

  on(name, listener) {
    if (!this.events.has(name)) this.events.set(name, new Set());
    this.events.get(name).add(listener);
    return this;
  }

  emit(name, ...args) {
    const listeners = this.events.get(name);
    if (listeners) {
      listeners.forEach(fn => fn(...args));
    }
  }

  off(name, listener) {
    const listeners = this.events.get(name);
    if (listeners) {
      listeners.delete(listener);
    }
  }

  once(name, listener) {
    const wrapper = (...args) => {
      this.off(name, wrapper);
      listener(...args);
    };
    this.on(name, wrapper);
  }
}
Interviewer Note: 💡 Check if their once() implementation correctly unsubscribes when triggered, preventing subsequent runs.

Coding Challenge #10: Flatten a nested object structure, returning dot-notated keys (e.g. { 'a.b.c': 1 })

Level: Coding Challenge | Category: Objects | Time: 20 mins

Expected Solution

Write a recursive function that flattens a nested object structure, concatenating nested keys using dot notation.

Code Example


function flattenObject(obj, prefix = '') {
  const result = {};
  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      const newKey = prefix ? `${prefix}.${key}` : key;
      if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
        Object.assign(result, flattenObject(obj[key], newKey));
      } else {
        result[newKey] = obj[key];
      }
    }
  }
  return result;
}
Interviewer Note: 💡 Look for proper hasOwnProperty checks to avoid iterating over inherited prototype properties.

Coding Challenge #11: Implement Promise.allSettled using only Promise.all and .then()

Level: Coding Challenge | Category: Async Programming | Time: 20 mins

Expected Answer

Promise.allSettled() waits for all promises to resolve or reject, returning an array of objects detailing the outcome of each promise. To implement it using Promise.all(), map input promises to wrapper promises that resolve successfully, returning status and value payloads.

Code Example


function allSettled(promises) {
  const wrappedPromises = promises.map(p => 
    Promise.resolve(p).then(
      value => ({ status: 'fulfilled', value }),
      reason => ({ status: 'rejected', reason })
    )
  );
  return Promise.all(wrappedPromises);
}
Interviewer Note: 💡 Ask the candidate why they wrap promises in Promise.resolve() (Answer: to handle non-promise raw values correctly).

Coding Challenge #12: Implement custom Array methods myMap, myFilter, and myReduce.

Level: Coding Challenge | Category: Arrays | Time: 20 mins

Expected Solution

Implement these methods on Array.prototype without using built-in array methods. Use loops and respect original argument signatures.

Code Example


Array.prototype.myMap = function(callback, thisArg) {
  const result = [];
  for (let i = 0; i < this.length; i++) {
    if (i in this) {
      result[i] = callback.call(thisArg, this[i], i, this);
    }
  }
  return result;
};
Interviewer Note: 💡 Look for sparse array safety (checking if index 'i' exists in 'this' to skip holes in arrays).

Anti-AI Scenario #3: Why is using delete object.property a performance antipattern in V8 Engine optimizing compilers?

Level: Anti-AI Validation | Category: V8 Engine | Time: 12 mins

Expected Answer

Using the delete keyword on object properties de-optimizes property accesses in V8:

  • Shape Invalidation: Objects share internal shape maps (hidden classes) to speed up property accesses. Deleting a property changes the object structure, invalidating its shape map.
  • Dictionary Mode: V8 transitions the object to **Dictionary Mode** (slow key-value hash map lookup) to prevent shape-change cascades. Property lookups bypass optimizations, running significantly slower.

Alternative

Instead of deleting, assign properties to undefined or use destructuring to create a new object shape without the target property.

Interviewer Note: 💡 V8 engine performance details. AI models often suggest delete to clear properties, ignoring hidden class invalidation hazards.

Anti-AI Scenario #4: Why does changing the shape of an object array elements prevent V8 from applying inline caching optimizations?

Level: Anti-AI Validation | Category: V8 Engine | Time: 12 mins

Expected Answer

V8 uses **Inline Caching (IC)** to optimize property accesses by caching property offsets for specific hidden classes (shapes).

  • Polymorphism: If elements in an array have different shapes (e.g. properties added in different order), the inline cache becomes polymorphic or megamorphic.
  • Performance: V8 must check multiple shape signatures at runtime, which slows down property lookups compared to monomorphic cache access.
Interviewer Note: 💡 Check if the candidate knows to instantiate objects with identical structures and property orders.

Anti-AI Scenario #5: Why does a generator function returning an asynchronous stream of promises leak memory if it's not closed explicitly?

Level: Anti-AI Validation | Category: Async Programming | Time: 12 mins

Expected Answer

A generator maintains a reference to its local execution scope (closure) while it is paused. If an asynchronous loop pulls values from a generator but exits early (e.g. via break or return) without executing to completion, the generator remains suspended. Because it is suspended, V8 preserves its entire scope closure (including variables, arguments, and in-flight promises) in memory, creating a memory leak.

Resolution: Always call generator.return() in cleanup blocks or use for await...of loops, which trigger generator disposal automatically on break.

Interviewer Note: 💡 Excellent test for async generator closures. AI tools often omit generator cleanup when designing dynamic pipelines.

Anti-AI Scenario #6: Why does setting the prototype of an object instance at runtime using Object.setPrototypeOf de-optimize all code paths calling that object?

Level: Anti-AI Validation | Category: V8 Engine | Time: 12 mins

Expected Answer

Altering prototypes at runtime using Object.setPrototypeOf() is a slow operation in all modern JavaScript engines. It invalidates cached prototype chains and object shape lookups throughout the application. All functions that have optimized access to the object are de-optimized and compiled back to slower execution paths.

Alternative: Define prototype structures at creation time using Object.create() instead of modifying them later.

Interviewer Note: 💡 Tests deep runtime optimizations. Candidates must explain shape invalidations across V8 caches.

Anti-AI Scenario #7: Why is using new Function() or eval() inside a loop de-optimized by V8 engines?

Level: Anti-AI Validation | Category: V8 Engine | Time: 11 mins

Expected Answer

Calling eval() or new Function() compiles strings to machine code dynamically at runtime. Executing this in a loop prevents V8's optimizing compilers (TurboFan) from performing compile-time optimizations (like inline caching, scope parsing, or dead-code elimination) because scopes and variables change on every run.

Interviewer Note: 💡 Explain why dynamically parsed code cannot be statically optimized by V8 compilers.

Anti-AI Scenario #8: Why does passing arguments from a function using arguments array slice de-optimize the function in older V8 but not in modern engines?

Level: Anti-AI Validation | Category: V8 Engine | Time: 11 mins

Expected Answer

In older V8 engines, passing the arguments object outside a function (e.g. Array.prototype.slice.call(arguments)) caused 'arguments leakage'. Because the arguments object provides dynamic access to local scope properties, passing it outside prevented V8 from optimizing variable allocations on the stack, forcing it to allocate scope on the heap instead.

Modern V8: The optimizing compiler now supports rest parameters (...args) natively, which optimizes array allocations without heap escape analysis issues.

Interviewer Note: 💡 Tests compilation history. Candidates should explain why rest syntax is preferred over the legacy arguments object.

Anti-AI Scenario #9: Why does Array.from({ length: 1000000 }) block the main thread, and how does generators/iterators avoid this?

Level: Anti-AI Validation | Category: Performance Optimization | Time: 12 mins

Expected Answer

Array.from() builds a complete, contiguous array in memory synchronously. For massive lengths, this blocks the main thread during allocation and initialization, freezing user interactions.

Generators: By yielding values lazily using generator functions, you process elements one at a time without allocating large arrays in memory, keeping execution light and responsive.

Interviewer Note: 💡 Look for candidate's strategies to process large datasets without main-thread blocking.

Anti-AI Scenario #10: Why does executing a function inside a try/catch block historically prevent V8 from inlining that function?

Level: Anti-AI Validation | Category: V8 Engine | Time: 12 mins

Expected Answer

Historically, V8's optimizing compiler (Crankshaft) did not support try/catch blocks. Any function containing a try/catch block was marked as de-optimized and could not be inlined into parent call stacks. This introduced function call overhead and restricted compiler optimizations.

Modern V8: The TurboFan compiler supports try/catch natively, but keeping try/catch blocks small remains a best practice to avoid de-optimizations in hot loops.

Interviewer Note: 💡 Tests V8 engine history. Ask how they isolate error-prone code blocks from hot path calculations.

Anti-AI Scenario #11: Why does assigning array elements to large indices (e.g., arr[1000000] = 1) immediately trigger dictionary mode in V8 arrays?

Level: Anti-AI Validation | Category: V8 Engine | Time: 12 mins

Expected Answer

V8 allocates arrays as contiguous memory slots to speed up index lookups. If you assign an element to a large index (e.g. arr[1000000] = 1) on a small array, allocating a million empty memory slots is highly inefficient. V8 converts the array to **Dictionary Mode**, storing index-value pairs as a hash table in memory. While this saves memory, it makes index lookups significantly slower.

Interviewer Note: 💡 Tests deep array optimization strategies in V8. Candidates should explain why sparse arrays degrade performance.

Anti-AI Scenario #12: Why does using a Map with complex object keys prevent garbage collection if the key object reference is deleted?

Level: Anti-AI Validation | Category: Memory Management | Time: 11 mins

Expected Answer

A standard Map maintains strong references to both its keys and values. If you delete all variables referencing a key object, the Map still holds a reference to it in memory. As a result, the key object and its value cannot be garbage-collected, which leads to a memory leak. Use a WeakMap instead to allow garbage collection.

Interviewer Note: 💡 Verify they understand the difference between strong and weak references.

Anti-AI Scenario #13: Why does calling console.log on complex objects inside high-frequency loops cause memory leaks in chromium engines?

Level: Anti-AI Validation | Category: Garbage Collection | Time: 11 mins

Expected Answer

Browsers preserve references to objects logged in the developer console so users can inspect their properties dynamically. Because the console holds active references, the logged objects cannot be garbage-collected. Logging objects repeatedly inside high-frequency loops causes memory usage to grow continuously, which can crash the browser tab.

Interviewer Note: 💡 A common debugging trap. Candidates should disable verbose console logs in production environments.

Anti-AI Scenario #14: Why does a microtask queue starvation occur if an async function repeatedly awaits microtasks, and why does macrotask schedules prevent browser freezing?

Level: Anti-AI Validation | Category: Event Loop | Time: 12 mins

Expected Answer

The event loop runs microtasks continuously until the microtask queue is empty. If an asynchronous function repeatedly schedules new microtasks (e.g. recursive Promise resolves), the event loop remains stuck in the microtask phase. Because it is stuck, it never reaches the macrotask or rendering phases, which freezes the browser and stops layout updates.

Macrotask Scheduling: Using setTimeout yields execution control back to the event loop, allowing the browser to render updates between iterations.

Interviewer Note: 💡 Check if the candidate knows how microtask queues are cleared compared to macrotasks.

Anti-AI Scenario #15: Why do ES Module imports support circular dependencies while CommonJS require results in partially empty object bindings?

Level: Anti-AI Validation | Category: Modules | Time: 12 mins

Expected Answer

These module systems handle imports and bindings differently:

  • ES Modules (ESM): Resolves imports as **read-only live bindings**. During static analysis, module references are linked before code execution. This allows circular imports to resolve correctly once modules evaluate.
  • CommonJS (CJS): Evaluates modules sequentially. require() returns a copy of the exported object. If circular dependency occurs, require returns an incomplete, partially evaluated object, leading to undefined exports.
Interviewer Note: 💡 Look for candidate's understanding of static compile-time linking (ESM) vs runtime execution (CommonJS).

Anti-AI Scenario #16: Why does using performance.now() yield non-precise timestamps in browsers, and how does it prevent Spectre attacks?

Level: Anti-AI Validation | Category: Security | Time: 11 mins

Expected Answer

Spectre side-channel attacks exploit timing differences in CPU caches to read secure memory values. Attackers require high-precision timers to execute these exploits. To mitigate this risk, browser vendors reduce the precision of performance.now() (e.g. rounding timestamps to the nearest 5-10 microseconds) and introduce random jitter to prevent micro-timing attacks.

Interviewer Note: 💡 Tests browser security and CPU side-channel awareness. An advanced topic that highlights browser-level security.

Anti-AI Scenario #17: Why does creating closures inside a high-frequency rendering loop trigger Garbage Collection thrashing?

Level: Anti-AI Validation | Category: Garbage Collection | Time: 12 mins

Expected Answer

Creating functions or closures inside a high-frequency rendering loop (running 60 times per second) allocates new function objects in memory on every frame. This rapid object allocation quickly fills the young generation memory space, triggering frequent scavenger garbage collection runs (GC thrashing). This blocks the main thread, causing frame rate drops (jank) and laggy animations.

Resolution: Define helper functions outside the rendering loop and pass necessary data as arguments to avoid creating closures on every frame.

Interviewer Note: 💡 A critical performance optimization topic. Candidates should explain why allocations inside rendering loops degrade performance.

Anti-AI Scenario #18: Why does const fn = Function.prototype.bind.call(target) execute faster than normal function wrapper calls?

Level: Anti-AI Validation | Category: V8 Engine | Time: 12 mins

Expected Answer

Calling Function.prototype.bind.call() creates direct, optimized context bindings in the engine. This avoids the overhead of creating additional JavaScript closures and wrapping nested functions, allowing V8 compilers to inline function executions directly.

Interviewer Note: 💡 Tests deep runtime optimizations. Look for candidate's knowledge of function call structures in V8.

Anti-AI Scenario #19: Why does an object definition with numeric keys like { 1: 'a', 2: 'b' } get ordered differently than string keys during Object.keys() serialization?

Level: Anti-AI Validation | Category: Objects | Time: 12 mins

Expected Answer

According to ECMAScript specifications, object properties are ordered based on key type during iteration (e.g. Object.keys(), JSON.stringify()):

  • Integer keys: Sorted in ascending numerical order.
  • String keys: Ordered in chronological insertion order.
  • Symbol keys: Sorted in chronological insertion order.

The Trap: Mixing numeric and string keys can lead to unexpected property ordering during serializations.

Interviewer Note: 💡 Ask the candidate how they would preserve insertion order when using numeric keys (Answer: Use a Map instead of an Object).

Frequently Asked Questions

Are these JavaScript interview questions suitable for experienced developers?

Yes. The guide covers beginner, intermediate, senior, and advanced JavaScript concepts commonly asked in interviews.

Do these questions cover modern JavaScript (ES6+)?

Yes. Topics include ES6+, async/await, promises, modules, destructuring, optional chaining, and other modern JavaScript features.

Are core JavaScript concepts included?

Yes. It covers closures, hoisting, scope, prototypes, this keyword, event loop, execution context, and memory management.

Are practical coding exercises included?

Yes. The guide includes coding challenges, debugging questions, DOM manipulation, and real-world problem-solving exercises.

Does it include JavaScript interview questions for frontend developers?

Yes. It includes browser APIs, DOM events, asynchronous programming, performance optimization, and topics frequently asked in frontend interviews.