FE Lead prep · ref 12 · §2.6 · rapid-fire baseline-credibility questions
The frame — lexical vs runtime
Closures / scope / TDZ = LEXICAL (decided by where written). this / prototype chain = RUNTIME (decided by how called). Different questions, different times.
var / let / const + TDZ
var
let / const
Scope
function
block
Hoist
→ undefined
→ TDZ (ReferenceError if read early)
const locks the binding, not the value: const a=[];a.push(1) ✓; a=[] ✗.
Closures
Function + lexical env it was declared in; keeps outer vars alive after outer returns. Powers privacy, factories, useState.
Leak risk: closed-over vars (DOM, arrays) can't be GC'd while closure lives (L08).
this — call site decides (4 rules, high→low)
Call
this
new Fn()
new instance
fn.call/apply/bind(o)
explicitly o
obj.fn()
obj (before the dot)
fn() bare
undefined (strict) / global
Arrow = no own this → uses enclosing lexicalthis (+ no own arguments, can't new). Ideal for callbacks inside a method.
const f = obj.hi; f(); // ✗ undefined — lost this
setTimeout(()=>obj.hi(), 0); // ✓ arrow keeps call form
setTimeout(obj.hi.bind(obj), 0); // ✓ bind pins this
Prototypes
Objects linked to objects; property read walks the chain until found or null. class = sugar; methods live once on the prototype, shared. extends links prototypes; instanceof checks the chain.
ESM vs CJS (static vs dynamic)
ESM import/export
CJS require
Resolved
static, pre-exec
runtime call
Load
async
sync
Binding
live
copied value
Mode
strict + top-level await
sloppy
Tree-shake
yes
not reliably
Why ESM tree-shakes: static structure ⇒ bundler knows used/unused exports without running code ⇒ drops dead. CJS require(x) can be dynamic ⇒ can't prove unused (L05; + sideEffects:false).
Event delegation
list.addEventListener('click', e => {
const li = e.target.closest('li'); // bubbling + target
if (li) select(li.dataset.id); // one handler, all current+future rows
});
Why: 1 listener not N (memory/setup, L07/L08) + auto-covers dynamically added/removed rows (virtualized lists). Caveat: needs bubbling (focus/blur don't → use focusin); filter the target.
3 phases:capture (root→target, top-down) → target → bubble (target→root, bottom-up). addEventListener = bubble by default (why delegation works); {capture:true} fires first (interception). target=what triggered (clicked child) vs currentTarget=where handler is (parent). stopPropagation halts trip but same-element listeners still run; stopImmediatePropagation stops those too. Delegate non-bubbling (focus/blur) via focusin/focusout OR the capture phase.
Lead one-liners (memorize)
Frame "Closures are lexical — fixed by where written; this is dynamic — fixed by how it's called. Different questions at different times."
Closure "A function that remembers where it was born. var in a loop shares one binding (3 3 3); let gives each its own (0 1 2)."
this "Set by the call site, not the definition. obj.fn() binds obj; a bare fn() loses it; arrows borrow the enclosing scope's."
Modules "ESM is static and analyzable; CJS is a runtime call. That's the whole reason ESM tree-shakes and CJS doesn't."
Lead "Codify the mechanics into lint rules + conventions — arrow class fields, hooks-deps lint, ESM-only — so the bugs are un-writable, not policed per PR."