{}The Interview
Handbook

Tracks / JavaScript

Browser, DOM, modules & tooling

mid 10 questions · 5 min read browserdommodulescors

Questions in this set 10
  1. 01Walk me through what happens when you type a URL and press enter.
  2. 02Explain defer vs async vs a plain <script>.
  3. 03CommonJS vs ES modules.
  4. 04localStorage vs sessionStorage vs cookies vs IndexedDB.
  5. 05Explain CORS. Why does my request fail with "No 'Access-Control-Allow-Origin' header"?
  6. 06What is the difference between innerHTML, textContent and innerText?
  7. 07How do you avoid layout thrashing?
  8. 08What is the difference between Map/Set and plain objects/arrays?
  9. 09What are Web Workers and Service Workers?
  10. 10How does JavaScript garbage collection work, and what causes leaks?
01

Walk me through what happens when you type a URL and press enter.

The canonical whiteboard question. Structure the answer in stages and go as deep as they push:

  1. URL parse → scheme, host, path. Browser checks HSTS preload (may force https).
  2. DNS — browser cache → OS cache → resolver → root/TLD/authoritative. Returns an IP (or a CDN's anycast IP).
  3. TCP handshake (or QUIC over UDP for HTTP/3), then TLS — ClientHello/ServerHello, certificate validation against the trust store, key exchange, ALPN negotiates HTTP/2 or /3. TLS 1.3 does this in one round trip, zero with resumption.
  4. HTTP request → CDN edge / load balancer → app server; response with status, headers (cache-control, content-encoding, set-cookie), body.
  5. Render: parse HTML → DOM; CSS → CSSOM (render-blocking); scripts block the parser unless defer/async/type=module. DOM + CSSOM → render tree → layout (geometry) → paintcomposite (GPU layers).
  6. Post-load: DOMContentLoaded after parsing + deferred scripts; load after all subresources; hydration if it is an SSR framework app.

Mention the metrics that follow from this: TTFB (steps 1-4), FCP, LCP (largest element painted), CLS (layout stability), INP (interaction responsiveness).

02

Explain defer vs async vs a plain <script>.

downloads executes order preserved blocks parser
<script> immediately immediately yes yes
async parallel as soon as it lands no briefly, on execution
defer parallel after parsing, before DOMContentLoaded yes no
type="module" parallel deferred by default yes no

async for independent things (analytics); defer for app code that needs the DOM and depends on order. Scripts at the end of <body> are the legacy version of defer.

03

CommonJS vs ES modules.

CJS (require/module.exports) is synchronous, dynamic (you can require conditionally), and resolves at runtime — its exports are a mutable object. ESM (import/export) is static: the dependency graph is known before evaluation, which enables tree-shaking, cyclic-import handling via live bindings, and top-level await.

js
import { a } from "./m.js";     // hoisted, static, live binding
const m = await import("./m.js");   // dynamic import: async, code-splitting point

Practical friction to mention: you can import CJS from ESM (default-only interop), but not require ESM synchronously (Node 22+ can, under conditions); __dirname does not exist in ESM (import.meta.dirname); and package "type": "module" plus exports maps decide which one a file is.

04

localStorage vs sessionStorage vs cookies vs IndexedDB.

size lifetime sent to server API
cookie ~4 KB Expires/Max-Age yes, every request string
localStorage ~5-10 MB until cleared no sync, string
sessionStorage ~5-10 MB per tab no sync, string
IndexedDB large (quota-based) until cleared no async, structured

Security: never store a session token in localStorage — any XSS reads it. Use an HttpOnly; Secure; SameSite=Lax cookie so JavaScript cannot touch it. localStorage is also synchronous and blocks the main thread; for anything sizeable use IndexedDB (or a wrapper like idb).

05

Explain CORS. Why does my request fail with "No 'Access-Control-Allow-Origin' header"?

The same-origin policy blocks a page from reading responses from a different origin (scheme+host+port). CORS is the server's way to opt in. For "non-simple" requests (custom headers, PUT/DELETE, Content-Type: application/json) the browser first sends an OPTIONS preflight; the server must answer with Access-Control-Allow-Origin/Methods/Headers, and Access-Control-Max-Age to cache that answer.

Key points that separate a good answer:

  • CORS is enforced by the browser, on the response. The request often reaches your server and executes — so it is not a substitute for authorisation. curl and server-to-server calls are unaffected.
  • For cookies you need credentials: "include" on the client and Access-Control-Allow-Credentials: true with an explicit origin (* is rejected) on the server.
  • The fix is a server config change, never a frontend one. A dev proxy (Vite's server.proxy) sidesteps it locally by making the request same-origin.
06

What is the difference between innerHTML, textContent and innerText?

innerHTML parses markup — an XSS hole with untrusted data. textContent gets/sets raw text of all nodes including hidden ones, and is fast. innerText is layout-aware (respects CSS visibility, collapses whitespace) and therefore triggers reflow when read — a real performance bug in loops. Default to textContent.

07

How do you avoid layout thrashing?

Reading a layout property (offsetHeight, getBoundingClientRect, scrollTop, getComputedStyle) forces the browser to flush pending style/layout work. Interleaving reads and writes in a loop causes a synchronous layout per iteration.

js
// BAD: read/write/read/write -> forced synchronous layout each time
els.forEach(el => { el.style.height = el.offsetHeight * 2 + "px"; });

// GOOD: batch reads, then batch writes
const heights = els.map(el => el.offsetHeight);
els.forEach((el, i) => { el.style.height = heights[i] * 2 + "px"; });

Also: animate transform/opacity (compositor-only, no layout or paint), use content-visibility/will-change judiciously, and prefer IntersectionObserver and ResizeObserver over scroll/resize handlers that measure.

08

What is the difference between Map/Set and plain objects/arrays?

Map keys can be any type (including objects), preserves insertion order, has a real size, and is optimised for frequent adds/deletes; objects have string/symbol keys, a prototype chain (watch for __proto__ and prototype-pollution), and are better for records and JSON. Set gives O(1) membership and dedup ([...new Set(arr)]). WeakMap/WeakSet hold keys weakly — the right tool for attaching metadata to DOM nodes or objects without leaking them.

09

What are Web Workers and Service Workers?

Web Worker: a background thread with no DOM access, communicating via postMessage (structured clone) — for CPU work like parsing, compression, image processing. Service Worker: a proxy between the page and the network, event-driven, for offline caching, background sync and push. It has a lifecycle (install → activate → fetch), can serve stale content if you get the caching strategy wrong, and must be served over HTTPS. Do not put a service worker on a site until you understand cache invalidation — a bad one can pin users to a broken build.

10

How does JavaScript garbage collection work, and what causes leaks?

A mark-and-sweep collector traces from roots (globals, stack, active closures); anything unreachable is freed, with generational/incremental refinements in V8. Common leaks: forgotten timers and setInterval, listeners not removed on teardown, closures holding large objects, detached DOM nodes still referenced by JS, and unbounded caches/arrays used as logs. Find them with Chrome DevTools Memory → heap snapshots taken before/after an action, comparing retained size and looking for "Detached HTMLDivElement".