` in the HTML simply won't execute — the browser blocks it and logs a CSP violation.\n\nCSP is a defense-in-depth layer, not a replacement for proper input sanitization/escaping — you still need to prevent the injection in the first place (e.g. never using `dangerouslySetInnerHTML` with unsanitized user content), but CSP gives you a second line of defense that limits the blast radius if an XSS bug slips through."}},{"@type":"Question","name":"What's the difference between XSS and CSRF, and how do you defend against each?","acceptedAnswer":{"@type":"Answer","text":"XSS (Cross-Site Scripting) is about an attacker getting their JavaScript to run in *your* page, in the victim's browser session — typically by injecting a script through unsanitized user input that later gets rendered as HTML (a comment field, a search query reflected back, etc.). Once that script runs, it has full access to the page's DOM, cookies (unless `HttpOnly`), and can make authenticated requests as the victim. Defense: escape/sanitize all user-generated content before rendering it as HTML (React does this by default for text content; the danger is specifically `dangerouslySetInnerHTML` or raw `innerHTML`), plus CSP as a second layer.\n\nCSRF (Cross-Site Request Forgery) doesn't need to inject any code at all — it tricks the victim's browser into sending a legitimate, authenticated request to *your* site from a *different*, malicious site, relying on the browser automatically attaching cookies to same-site requests. E.g. a malicious page auto-submits a hidden form to `bank.com/transfer` — if the victim is logged into `bank.com`, their browser sends the request with valid session cookies attached, and the bank has no way to tell it wasn't intentional.\n\n```\n// Defense: CSRF tokens (server issues a token, checks it on state-changing requests)\n// and SameSite cookies, which stop the browser from attaching them to cross-site requests at all\nSet-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly\n```\n\nThe short version: XSS is about executing attacker code in your origin; CSRF is about forging requests that ride on the victim's existing authenticated session without any code execution needed."}},{"@type":"Question","name":"Explain the JavaScript event loop, and the difference between microtasks and macrotasks.","acceptedAnswer":{"@type":"Answer","text":"JavaScript runs on a single thread with a call stack, but asynchronous work (timers, I/O, promises) is handled by the browser/runtime outside that stack, coordinated via the event loop. When the call stack is empty, the event loop pulls the next task from a queue and runs it. There are two queues that matter here: the microtask queue (Promise callbacks, `queueMicrotask`, `async/await` continuations) and the macrotask queue (`setTimeout`, `setInterval`, I/O callbacks, UI events).\n\nThe key rule: after each single macrotask finishes, the event loop drains the ENTIRE microtask queue completely — including any new microtasks added while draining — before running even one more macrotask or repainting the UI. This is why promises always \"jump ahead\" of `setTimeout(fn, 0)`, no matter how the code is ordered:\n\n```js\nconsole.log('1');\nsetTimeout(() => console.log('2'), 0); // macrotask — queued for later\nPromise.resolve().then(() => console.log('3')); // microtask — runs before the next macrotask\nconsole.log('4');\n// Output: 1, 4, 3, 2\n```\n\nThis distinction explains real bugs: an infinite chain of `.then()` callbacks that keep scheduling more microtasks can starve the event loop entirely, blocking `setTimeout` callbacks and even UI rendering from ever getting a turn, since the browser won't move to the next macrotask (or repaint) until the microtask queue is fully empty."}},{"@type":"Question","name":"What's the difference between `Object.freeze()` and `const`?","acceptedAnswer":{"@type":"Answer","text":"`const` only prevents *reassigning the variable binding* — it says nothing about the value's internal mutability. `Object.freeze()` operates on the value itself, making an object's existing top-level properties non-writable, non-configurable, and preventing new properties from being added — but it's shallow, so nested objects inside a frozen object are still fully mutable.\n\n```js\nconst obj = Object.freeze({ name: 'Alice', address: { city: 'NYC' } });\nobj.name = 'Bob'; // silently fails (throws in strict mode)\nobj.address.city = 'LA'; // WORKS — freeze is shallow, nested object isn't frozen\nconsole.log(obj.address.city); // 'LA'\n\nconst arr = ['a', 'b'];\narr.push('c'); // fine — const only blocks reassigning `arr`, not mutating it\n```\n\nSo `const arr = []` and mutating it with `.push()` is completely legal, which surprises people expecting `const` to mean \"immutable.\" For real deep immutability you'd need to recursively freeze every nested object yourself, or reach for a library/pattern (Immer, `structuredClone` + freeze, or just consistently creating new objects instead of mutating) — `Object.freeze` alone at one level rarely gives you the guarantee people assume it does."}},{"@type":"Question","name":"What are the basics of web accessibility (a11y) — ARIA roles, semantic HTML, and keyboard navigation?","acceptedAnswer":{"@type":"Answer","text":"The first rule of accessibility is to prefer semantic HTML over generic `
`s with JS-attached behavior, because semantic elements come with built-in accessibility behavior for free: `