JavaScript and TypeScript¶
Language core¶
var vs. let vs. const; hoisting; the temporal dead zone?
var is function-scoped and hoisted with value undefined. let/const are block-scoped and hoisted but
uninitialised until their line runs (the temporal dead zone: accessing them earlier throws). const prevents
reassignment, not mutation of the object. Function declarations are hoisted completely.
What is a closure? Give a practical use.
A function remembers the variables of the scope where it was created, even after that scope returns. Uses: private state, factories, partial application, callbacks and hooks.
function counter() { let n = 0; return () => ++n }
const next = counter(); next(); next() // 2
// Classic bug: var in a loop
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)) // 3 3 3
for (let i = 0; i < 3; i++) setTimeout(() => console.log(i)) // 0 1 2 (new binding per iteration)
React link: "stale closures": an effect or callback captures the state from the render it was created in.
How does this work? Arrow functions?
Decided by how the function is called: obj.f() → obj; plain f() → undefined in strict mode;
new F() → the new object; f.call(x) / apply / bind → x. Arrow functions have no own this: they use
the enclosing scope's, which is why they're used for callbacks.
Explain the event loop: microtasks vs. macrotasks. What does this print?
JS runs on one thread with a call stack. When the stack empties, the loop runs all microtasks (promise
callbacks, queueMicrotask, await continuations), then renders if needed, then takes one macrotask (timers,
I/O, UI events), and repeats.
Promises and async/await: how do you run things in parallel and handle errors?
await in sequence runs one after another; start both, then await, to run in parallel:
const [models, funds] = await Promise.all([getModels(), getFunds()]) // fails fast on first rejection
const results = await Promise.allSettled(requests) // every outcome, never rejects
const first = await Promise.race([fetchData(), timeout(2000)]) // first to settle
const any = await Promise.any(mirrors.map(fetch)) // first to fulfil
try/catch around await. An unhandled rejection is a bug; always return or await promises.
== vs. ===?
=== compares without type conversion; == converts types first ('1' == 1 is true, null == undefined is
true). Always use ===.
What is prototypal inheritance?
Every object has a link to a prototype; property lookups walk up the chain. class is syntax over prototypes.
Methods defined in a class live on Class.prototype, shared by instances.
Shallow vs. deep copy?
Spread ({...obj}, [...arr]) and Object.assign copy one level: nested objects are shared.
structuredClone(obj) makes a deep copy (handles dates, maps, cycles; not functions).
Write debounce and throttle.
function debounce(fn, ms) { // run after the calls stop for ms (search box)
let t
return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms) }
}
function throttle(fn, ms) { // run at most once per ms (scroll, resize)
let last = 0
return (...args) => {
const now = Date.now()
if (now - last >= ms) { last = now; fn(...args) }
}
}
What is event delegation?
Attach one listener to a parent and use event.target to handle events from many children, thanks to event
bubbling. Fewer listeners, works for elements added later. stopPropagation stops bubbling;
preventDefault stops the browser's default action.
map, filter, reduce: group items by key.
TypeScript¶
Why TypeScript? interface vs. type?
Catches errors at compile time, documents shapes, enables safe refactoring and better autocomplete. interface
is extendable and can be merged (good for object shapes and public APIs); type can express unions,
intersections, mapped and conditional types. Either is fine for objects; be consistent.
any vs. unknown vs. never?
any turns off checking. unknown accepts anything but must be narrowed before use (safe for parsed JSON).
never is a value that can't exist: return type of a function that throws, and the type left after exhaustive
narrowing, which makes the compiler check every case is handled:
Name utility types you use.
Partial<T>, Required<T>, Readonly<T>, Pick<T, K>, Omit<T, K>, Record<K, V>, ReturnType<typeof f>
(Prayog: RootState = ReturnType<typeof store.getState>), Awaited<T>, NonNullable<T>.
What are generics in TypeScript?
Type parameters that keep the relationship between input and output: function first<T>(xs: T[]): T | undefined.
Constrain with extends: function byId<T extends { id: string }>(xs: T[]).
Browser and build¶
What happens when you type a URL and press Enter?
DNS lookup → TCP (and TLS) handshake → HTTP request → server response → browser parses HTML into the DOM, CSS into
the CSSOM, runs scripts (blocking unless defer/async) → render tree → layout → paint → composite. Then
resources load, JS hydrates or renders the app.
What does Vite do differently from webpack?
In development it serves native ES modules and transforms files on demand (esbuild), so start-up is instant and HMR is fast regardless of app size. For production it bundles with Rollup (or Rolldown in newer versions): tree-shaking, code splitting, hashed file names for caching.
Cookies vs. localStorage vs. sessionStorage?
Cookies are sent with every request to their domain (≈4 KB), can be HttpOnly, Secure, SameSite.
localStorage persists until cleared (~5 MB), per origin, readable by JS. sessionStorage is per tab and cleared
when it closes. Never store secrets where JS can read them if XSS is a risk.
What is CORS and why does the browser enforce it?
The same-origin policy stops a page from reading responses from another origin. CORS lets a server opt in with
Access-Control-Allow-Origin etc.; non-simple requests send a preflight OPTIONS first. It protects users'
cookies and data, not the server. Fix it on the server (or proxy through the same origin, as Prayog does with
Traefik routing /api and the web app on one host).