JavaScript Fundamentals
1. var vs let vs const?
var— function-scoped, hoisted, can be redeclared. Avoid in new code.let— block-scoped, no redeclaration, reassignable.const— block-scoped, no reassignment. (Object contents can still mutate —constdoesn't freeze the object.)
2. == vs === ?
== does type coercion (0 == "0" is true). === checks value + type. Always use === in production code.
3. What is hoisting?
JavaScript moves var declarations and function declarations to the top of their scope during compilation. let / const are also hoisted but are in a temporal dead zone until their declaration line.
4. Closures — explain with an example.
A function that "closes over" variables from its enclosing scope.
function counter() {
let n = 0;
return () => ++n;
}
const c = counter();
c(); c(); c(); // 1, 2, 3 — n is private but persists
5. this rules in JavaScript?
- Arrow function —
thisinherited lexically from outer scope. - Method call (
obj.fn()) —thisisobj. - Plain call (
fn()) —thisisundefinedin strict mode, the global object otherwise. new Fn()—thisis the newly created instance.- Bound (
fn.bind(x)) —thisisxpermanently.
Event Loop and Async
6. Explain the event loop.
JS is single-threaded. The call stack runs synchronously. Async APIs (timers, fetch, I/O) queue callbacks. The event loop pops them when the stack is empty. Microtasks (Promise callbacks, queueMicrotask) drain before macrotasks (setTimeout, I/O).
7. Promise.all vs Promise.allSettled vs Promise.race?
Promise.all— resolves when ALL succeed; rejects on FIRST failure.Promise.allSettled— waits for ALL to finish, always resolves with status info.Promise.race— resolves/rejects as soon as ANY settles. Useful for timeouts.
8. async/await — what does it desugar to?
await pauses the async function and registers .then(resume) on the awaited value. The rest of the function body becomes the .then callback. There's no magic threading — it's syntactic sugar over Promise chains.
9. How would you implement Promise.all from scratch?
function pAll(arr) {
return new Promise((resolve, reject) => {
const out = []; let done = 0;
arr.forEach((p, i) =>
Promise.resolve(p).then(v => {
out[i] = v;
if (++done === arr.length) resolve(out);
}, reject)
);
});
}
Memory and Performance
10. What causes memory leaks in JS?
- Forgotten event listeners on detached DOM nodes.
- Closures holding large objects.
- Detached but referenced DOM nodes.
setIntervalnot cleared.- Global variables that grow without bound.
11. How do you optimise re-renders in React?
React.memo+ memoised props.useMemo/useCallbackfor expensive values.- Keep state local — don't lift higher than needed.
- Virtualise long lists (
react-window). - Split bundles with
React.lazy.
Modern JS
12. What's new in ES2024 / ES2025?
Array.prototype.group/groupBy— split arrays into objects by key.Object.groupBy/Map.groupBy— same for non-array iterables.Iterator helpers(.map, .filter, .take on iterators — lazy).Promise.withResolvers— clean way to expose resolve/reject outside.Setmethods —.union,.intersection,.difference.- Pipeline operator (proposal, behind a flag).
13. Modules — import vs require?
- CommonJS (
require) — Node's original; synchronous; runtime. - ES Modules (
import) — standardised; statically analysable; async; supported in browsers + Node 14+. Use ES Modules in new code.
14. Why use TypeScript over JavaScript?
- Compile-time type safety catches a class of bugs before they ship.
- Better IDE autocomplete + refactoring.
- Self-documenting APIs — fewer "what does this argument do?" questions.
DOM and Browsers
15. Event bubbling vs capturing?
DOM events fire from the document down to the target (capture phase), then from the target back up to the document (bubble phase). addEventListener(name, fn, true) listens in capture; default is bubble.
16. Debounce vs throttle?
- Debounce — wait until N ms after the LAST call before firing (search-as-you-type).
- Throttle — fire at most once per N ms regardless of call rate (scroll, resize).
17. localStorage vs sessionStorage vs cookies?
| localStorage | sessionStorage | Cookie | |
|---|---|---|---|
| Sent to server | No | No | Yes (every request) |
| Size | ~5 MB | ~5 MB | 4 KB |
| Lifetime | Forever | Tab | Configurable |
| Read by JS | Yes | Yes | Yes (unless HttpOnly) |
Real-World
18. How does CORS work?
Browser sends Origin: ... header. Server must respond with Access-Control-Allow-Origin: <matching origin> (and other allow-* headers) for the browser to expose the response to JS. Preflight OPTIONS requests check non-simple requests before the real one.
19. CSRF in a JS-heavy app — defence?
- Use
SameSite=Lax(orStrict) cookies. - For state-changing requests, send a custom header like
X-CSRF-Token— browsers won't add custom headers cross-origin without CORS approval. - Or use Bearer tokens in
Authorizationheader instead of cookies altogether.
20. Implement a deep clone.
const deep = (v) => structuredClone(v); // modern browsers + Node 17+
// fallback (no functions, no cycles):
const fallback = (v) => JSON.parse(JSON.stringify(v));
21. What is "tree shaking"?
A build-time process where the bundler removes code that isn't imported anywhere. Works only with ES Modules (static import statements) — not with require.
22. Service workers — what are they for?
A script the browser runs in the background, separate from the page. Used for offline caching, push notifications, background sync — the foundation of PWAs.
23. Walk me through fetch('/api/x').then(...) step by step.
- JS engine schedules an HTTP request.
- The function returns immediately with a Promise (request is still in flight).
- When the response headers arrive, the Promise resolves with a
Responseobject. .json()reads + parses the body — returns ANOTHER Promise.- The final
.thengets the parsed data.
24. How do you handle errors in async/await?
try {
const r = await fetch(url);
if (!r.ok) throw new Error(`HTTP ${r.status}`);
const data = await r.json();
} catch (e) { /* network OR parsing OR your throw */ }
25. Optional chaining + nullish coalescing — when to use which?
user?.profile?.email— safely access deep paths; returnsundefinedif anything is null/undefined.name ?? "Guest"— fall back ONLY when value is null/undefined (not "" or 0, unlike||).
