[{"data":1,"prerenderedAt":3182},["ShallowReactive",2],{"page-\u002Fjs\u002F26-performance-and-optimization":3},{"id":4,"title":5,"body":6,"description":30,"extension":3176,"meta":3177,"navigation":198,"path":3178,"seo":3179,"stem":3180,"__hash__":3181},"content\u002Fjs\u002F26-performance-and-optimization.md","26 — Performance & Optimization",{"type":7,"value":8,"toc":3143},"minimark",[9,13,287,477,645,703,763,1056,1260,1413,1529,1677,1910,1997,2315,2381,2456,2597,2676,2863,3061,3139],[10,11,5],"h1",{"id":12},"_26-performance-optimization",[14,15,17,22,185,236],"question-wrapper",{"language":16},"javascript",[18,19,21],"h3",{"id":20},"q1-after-the-code-below-runs-is-the-removed-button-eligible-for-garbage-collection","Q1. After the code below runs, is the removed button eligible for garbage collection?",[23,24,25],"code-wrapper",{"language":16},[26,27,31],"pre",{"className":28,"code":29,"language":16,"meta":30,"style":30},"language-javascript shiki shiki-themes github-light github-dark","function attachHandler() {\n  const btn = document.getElementById(\"submit\");\n  let clickCount = 0;\n  btn.addEventListener(\"click\", () => {\n    clickCount++;\n    console.log(clickCount);\n  });\n}\nattachHandler();\ndocument.getElementById(\"submit\").remove();\n","",[32,33,34,51,80,98,121,132,144,150,156,165],"code",{"__ignoreMap":30},[35,36,39,43,47],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"svdQ7","function",[35,44,46],{"class":45},"sIsaT"," attachHandler",[35,48,50],{"class":49},"ssxIu","() {\n",[35,52,54,57,61,64,67,70,73,77],{"class":37,"line":53},2,[35,55,56],{"class":41},"  const",[35,58,60],{"class":59},"snvgF"," btn",[35,62,63],{"class":41}," =",[35,65,66],{"class":49}," document.",[35,68,69],{"class":45},"getElementById",[35,71,72],{"class":49},"(",[35,74,76],{"class":75},"sJ6F3","\"submit\"",[35,78,79],{"class":49},");\n",[35,81,83,86,89,92,95],{"class":37,"line":82},3,[35,84,85],{"class":41},"  let",[35,87,88],{"class":49}," clickCount ",[35,90,91],{"class":41},"=",[35,93,94],{"class":59}," 0",[35,96,97],{"class":49},";\n",[35,99,101,104,107,109,112,115,118],{"class":37,"line":100},4,[35,102,103],{"class":49},"  btn.",[35,105,106],{"class":45},"addEventListener",[35,108,72],{"class":49},[35,110,111],{"class":75},"\"click\"",[35,113,114],{"class":49},", () ",[35,116,117],{"class":41},"=>",[35,119,120],{"class":49}," {\n",[35,122,124,127,130],{"class":37,"line":123},5,[35,125,126],{"class":49},"    clickCount",[35,128,129],{"class":41},"++",[35,131,97],{"class":49},[35,133,135,138,141],{"class":37,"line":134},6,[35,136,137],{"class":49},"    console.",[35,139,140],{"class":45},"log",[35,142,143],{"class":49},"(clickCount);\n",[35,145,147],{"class":37,"line":146},7,[35,148,149],{"class":49},"  });\n",[35,151,153],{"class":37,"line":152},8,[35,154,155],{"class":49},"}\n",[35,157,159,162],{"class":37,"line":158},9,[35,160,161],{"class":45},"attachHandler",[35,163,164],{"class":49},"();\n",[35,166,168,171,173,175,177,180,183],{"class":37,"line":167},10,[35,169,170],{"class":49},"document.",[35,172,69],{"class":45},[35,174,72],{"class":49},[35,176,76],{"class":75},[35,178,179],{"class":49},").",[35,181,182],{"class":45},"remove",[35,184,164],{"class":49},[186,187,190,212,218,227],"ul",{"className":188},[189],"contains-task-list",[191,192,195,200,201,203,204,207,208,211],"li",{"className":193},[194],"task-list-item",[196,197],"input",{"disabled":198,"type":199},true,"checkbox"," No — the closure passed to ",[32,202,106],{}," still holds a reference to ",[32,205,206],{},"btn"," (and ",[32,209,210],{},"clickCount","), keeping the detached node alive until the listener is removed or the reference is released",[191,213,215,217],{"className":214},[194],[196,216],{"disabled":198,"type":199}," Yes, once removed from the DOM it's eligible immediately regardless of listeners",[191,219,221,223,224,226],{"className":220},[194],[196,222],{"disabled":198,"type":199}," Yes, because ",[32,225,210],{}," is a primitive and doesn't affect garbage collection",[191,228,230,232,233,235],{"className":229},[194],[196,231],{"disabled":198,"type":199}," No, because ",[32,234,69],{}," caches all elements internally forever",[237,238,239,243,257],"details",{},[240,241,242],"summary",{},"Show Answer",[244,245,246,250,251,203,253,207,255,211],"p",{},[247,248,249],"strong",{},"Answer:"," A — No — the closure passed to ",[32,252,106],{},[32,254,206],{},[32,256,210],{},[244,258,259,262,263,266,267,269,270,272,273,275,276,278,279,282,283,286],{},[247,260,261],{},"Explanation:"," ",[247,264,265],{},"Debug:"," Removing an element from the DOM tree does not remove any event listeners attached to it, and the closure created inside ",[32,268,161],{}," still captures ",[32,271,206],{}," in its scope for as long as the listener function itself is reachable — which it is, since the DOM's internal listener registry keeps a reference to it. This is the classic \"detached DOM node\" leak: the node is invisible on the page but still resident in memory. Option B is the common misconception that DOM removal alone triggers collection. Option C is irrelevant — a captured primitive doesn't change how ",[32,274,206],{}," is retained. Option D fabricates caching behavior ",[32,277,69],{}," doesn't have. The fix is to call ",[32,280,281],{},"btn.removeEventListener(...)"," (or use an ",[32,284,285],{},"AbortController"," signal) before\u002Fwhen discarding the node.",[14,288,289,297,405,443],{"language":16},[18,290,292,293,296],{"id":291},"q2-whats-the-memorybehavior-consequence-of-removing-clockel-without-clearing-the-interval","Q2. What's the memory\u002Fbehavior consequence of removing ",[32,294,295],{},"clockEl"," without clearing the interval?",[23,298,299],{"language":16},[26,300,302],{"className":28,"code":301,"language":16,"meta":30,"style":30},"function startPolling(el) {\n  setInterval(() => {\n    el.textContent = new Date().toLocaleTimeString();\n  }, 1000);\n}\nconst clockEl = document.getElementById(\"clock\");\nstartPolling(clockEl);\nclockEl.remove();\n",[32,303,304,320,332,353,363,367,388,396],{"__ignoreMap":30},[35,305,306,308,311,313,317],{"class":37,"line":38},[35,307,42],{"class":41},[35,309,310],{"class":45}," startPolling",[35,312,72],{"class":49},[35,314,316],{"class":315},"sCrzJ","el",[35,318,319],{"class":49},") {\n",[35,321,322,325,328,330],{"class":37,"line":53},[35,323,324],{"class":45},"  setInterval",[35,326,327],{"class":49},"(() ",[35,329,117],{"class":41},[35,331,120],{"class":49},[35,333,334,337,339,342,345,348,351],{"class":37,"line":82},[35,335,336],{"class":49},"    el.textContent ",[35,338,91],{"class":41},[35,340,341],{"class":41}," new",[35,343,344],{"class":45}," Date",[35,346,347],{"class":49},"().",[35,349,350],{"class":45},"toLocaleTimeString",[35,352,164],{"class":49},[35,354,355,358,361],{"class":37,"line":100},[35,356,357],{"class":49},"  }, ",[35,359,360],{"class":59},"1000",[35,362,79],{"class":49},[35,364,365],{"class":37,"line":123},[35,366,155],{"class":49},[35,368,369,372,375,377,379,381,383,386],{"class":37,"line":134},[35,370,371],{"class":41},"const",[35,373,374],{"class":59}," clockEl",[35,376,63],{"class":41},[35,378,66],{"class":49},[35,380,69],{"class":45},[35,382,72],{"class":49},[35,384,385],{"class":75},"\"clock\"",[35,387,79],{"class":49},[35,389,390,393],{"class":37,"line":146},[35,391,392],{"class":45},"startPolling",[35,394,395],{"class":49},"(clockEl);\n",[35,397,398,401,403],{"class":37,"line":152},[35,399,400],{"class":49},"clockEl.",[35,402,182],{"class":45},[35,404,164],{"class":49},[186,406,408,414,423,431],{"className":407},[189],[191,409,411,413],{"className":410},[194],[196,412],{"disabled":198,"type":199}," The interval automatically stops once its target element is detached from the DOM",[191,415,417,419,420,422],{"className":416},[194],[196,418],{"disabled":198,"type":199}," The interval keeps firing forever, and its closure keeps a live reference to ",[32,421,295],{},", preventing the detached node from ever being garbage collected — a classic leak",[191,424,426,262,428,430],{"className":425},[194],[196,427],{"disabled":198,"type":199},[32,429,295],{}," becomes eligible for garbage collection immediately since it no longer has a parent",[191,432,434,262,436,439,440,442],{"className":433},[194],[196,435],{"disabled":198,"type":199},[32,437,438],{},"setInterval"," throws once ",[32,441,316],{}," is detached, alerting you to the leak",[237,444,445,447,454],{},[240,446,242],{},[244,448,449,451,452,422],{},[247,450,249],{}," B — The interval keeps firing forever, and its closure keeps a live reference to ",[32,453,295],{},[244,455,456,262,458,262,460,462,463,466,467,469,470,472,473,476],{},[247,457,261],{},[247,459,265],{},[32,461,438],{}," has no awareness of the DOM — it keeps invoking its callback on schedule until ",[32,464,465],{},"clearInterval"," is explicitly called, regardless of whether the elements it touches are still attached. The arrow function's closure over ",[32,468,316],{}," keeps ",[32,471,295],{}," reachable forever, so the detached node (and everything it references) leaks for the lifetime of the page. Option A and D invent automatic cleanup\u002Ferror behavior that doesn't exist. Option C ignores that the interval's closure is still a live reference even after DOM removal. The fix is to store the interval id and call ",[32,474,475],{},"clearInterval(id)"," whenever the element is torn down (e.g., in a component's cleanup\u002Funmount logic).",[14,478,479,483,583,623],{"language":16},[18,480,482],{"id":481},"q3-over-a-long-running-session-with-thousands-of-unique-ids-whats-the-risk-with-this-cache-and-how-should-it-be-fixed","Q3. Over a long-running session with thousands of unique ids, what's the risk with this cache, and how should it be fixed?",[23,484,485],{"language":16},[26,486,488],{"className":28,"code":487,"language":16,"meta":30,"style":30},"const cache = new Map();\nfunction getUser(id) {\n  if (cache.has(id)) return cache.get(id);\n  const user = fetchUserSync(id);\n  cache.set(id, user);\n  return user;\n}\n",[32,489,490,506,520,546,560,571,579],{"__ignoreMap":30},[35,491,492,494,497,499,501,504],{"class":37,"line":38},[35,493,371],{"class":41},[35,495,496],{"class":59}," cache",[35,498,63],{"class":41},[35,500,341],{"class":41},[35,502,503],{"class":45}," Map",[35,505,164],{"class":49},[35,507,508,510,513,515,518],{"class":37,"line":53},[35,509,42],{"class":41},[35,511,512],{"class":45}," getUser",[35,514,72],{"class":49},[35,516,517],{"class":315},"id",[35,519,319],{"class":49},[35,521,522,525,528,531,534,537,540,543],{"class":37,"line":82},[35,523,524],{"class":41},"  if",[35,526,527],{"class":49}," (cache.",[35,529,530],{"class":45},"has",[35,532,533],{"class":49},"(id)) ",[35,535,536],{"class":41},"return",[35,538,539],{"class":49}," cache.",[35,541,542],{"class":45},"get",[35,544,545],{"class":49},"(id);\n",[35,547,548,550,553,555,558],{"class":37,"line":100},[35,549,56],{"class":41},[35,551,552],{"class":59}," user",[35,554,63],{"class":41},[35,556,557],{"class":45}," fetchUserSync",[35,559,545],{"class":49},[35,561,562,565,568],{"class":37,"line":123},[35,563,564],{"class":49},"  cache.",[35,566,567],{"class":45},"set",[35,569,570],{"class":49},"(id, user);\n",[35,572,573,576],{"class":37,"line":134},[35,574,575],{"class":41},"  return",[35,577,578],{"class":49}," user;\n",[35,580,581],{"class":37,"line":146},[35,582,155],{"class":49},[186,584,586,596,605,615],{"className":585},[189],[191,587,589,591,592,595],{"className":588},[194],[196,590],{"disabled":198,"type":199}," None — ",[32,593,594],{},"Map"," automatically evicts old entries once memory pressure is detected",[191,597,599,601,602,604],{"className":598},[194],[196,600],{"disabled":198,"type":199}," This can never leak because ",[32,603,517],{}," is typically a primitive number",[191,606,608,610,611,614],{"className":607},[194],[196,609],{"disabled":198,"type":199}," The cache grows unbounded since entries are never removed, steadily increasing memory use; fix by adding an eviction policy (LRU, TTL, or a max-size cap) — a ",[32,612,613],{},"WeakMap"," would not help here since the keys are primitive ids, not objects being tracked elsewhere",[191,616,618,262,620,622],{"className":617},[194],[196,619],{"disabled":198,"type":199},[32,621,594],{}," keys are always garbage collected once the function returns, so this is safe",[237,624,625,627,634],{},[240,626,242],{},[244,628,629,631,632,614],{},[247,630,249],{}," C — The cache grows unbounded since entries are never removed, steadily increasing memory use; fix by adding an eviction policy (LRU, TTL, or a max-size cap) — a ",[32,633,613],{},[244,635,636,638,639,641,642,644],{},[247,637,261],{}," A ",[32,640,594],{}," never evicts entries on its own — it holds a strong reference to every key and value for as long as the entry exists, so an ever-growing set of unique ids means an ever-growing cache with no upper bound. Option A invents automatic eviction that ",[32,643,594],{}," does not provide. Option B and D wrongly assume primitive keys are somehow exempt from retention — primitives are stored and retained just like any other value. The real fix is bounding growth explicitly (an LRU cache, a TTL-based expiry, or a max-entry cap that evicts the oldest\u002Fleast-used entry).",[14,646,647,651,683],{},[18,648,650],{"id":649},"q4-youre-implementing-search-as-you-type-autocomplete-that-should-fire-an-api-request-only-once-the-user-pauses-typing-for-300ms-not-on-every-keystroke-which-technique-fits-and-why","Q4. You're implementing search-as-you-type autocomplete that should fire an API request only once the user pauses typing for 300ms, not on every keystroke. Which technique fits, and why?",[186,652,654,660,666,672],{"className":653},[189],[191,655,657,659],{"className":656},[194],[196,658],{"disabled":198,"type":199}," Throttle — it guarantees the function runs at least once every 300ms even during continuous typing",[191,661,663,665],{"className":662},[194],[196,664],{"disabled":198,"type":199}," Throttle — it runs the function immediately on the first keystroke and ignores the rest",[191,667,669,671],{"className":668},[194],[196,670],{"disabled":198,"type":199}," Debounce — it runs the function on every keystroke but batches the results",[191,673,675,677,678,682],{"className":674},[194],[196,676],{"disabled":198,"type":199}," Debounce — it delays execution until 300ms have passed with no new keystrokes, so an idle pause is what finally triggers the call; throttle would still fire repeatedly at intervals ",[679,680,681],"em",{},"while"," the user keeps typing, which isn't what's wanted here",[237,684,685,687,694],{},[240,686,242],{},[244,688,689,691,692,682],{},[247,690,249],{}," D — Debounce — it delays execution until 300ms have passed with no new keystrokes, so an idle pause is what finally triggers the call; throttle would still fire repeatedly at intervals ",[679,693,681],{},[244,695,696,698,699,702],{},[247,697,261],{}," Debounce resets its timer on every call and only fires once the calls stop for the configured wait — exactly what \"wait until the user pauses\" needs. Throttle (options A and B) instead guarantees execution on a steady cadence ",[679,700,701],{},"during"," continuous activity, which would fire requests mid-typing rather than waiting for a pause — the two are frequently confused because both \"limit\" how often a function runs, but they solve opposite problems. Option C misdescribes debounce as running on every keystroke, which defeats its entire purpose.",[14,704,705,709,739],{},[18,706,708],{"id":707},"q5-a-scroll-handler-recalculates-a-sticky-headers-position-and-needs-to-run-at-most-once-every-100ms-while-the-user-is-actively-scrolling-not-only-after-scrolling-stops-which-is-correct","Q5. A scroll handler recalculates a sticky header's position and needs to run at most once every 100ms while the user is actively scrolling, not only after scrolling stops. Which is correct?",[186,710,712,718,724,733],{"className":711},[189],[191,713,715,717],{"className":714},[194],[196,716],{"disabled":198,"type":199}," Throttle with a 100ms interval — the handler executes on a steady cadence during continuous scrolling instead of only after it stops, keeping the header visually in sync while scrolling happens",[191,719,721,723],{"className":720},[194],[196,722],{"disabled":198,"type":199}," Debounce with a 100ms wait — ensures the handler only runs after scrolling stops",[191,725,727,262,729,732],{"className":726},[194],[196,728],{"disabled":198,"type":199},[32,730,731],{},"requestIdleCallback"," — runs the handler only when the browser is completely idle",[191,734,736,738],{"className":735},[194],[196,737],{"disabled":198,"type":199}," Neither — scroll handlers should never be rate-limited since browsers already throttle them to the display refresh rate automatically",[237,740,741,743,748],{},[240,742,242],{},[244,744,745,747],{},[247,746,249],{}," A — Throttle with a 100ms interval — the handler executes on a steady cadence during continuous scrolling instead of only after it stops, keeping the header visually in sync while scrolling happens",[244,749,750,752,753,755,756,758,759,762],{},[247,751,261],{}," Throttle guarantees the function runs at a capped rate ",[679,754,681],{}," events keep firing, which is exactly what a sticky header needs — visual updates during the scroll, not just at the end. Option B (debounce) would make the header appear frozen until scrolling fully stops, which looks broken for a \"stays in sync\" requirement. Option C is wrong because ",[32,757,731],{}," only runs during idle gaps and offers no guarantee of running during continuous scrolling at all. Option D is a myth — native ",[32,760,761],{},"scroll"," events can fire far more often than needed for visual updates, and manual rate-limiting is standard practice, not redundant.",[14,764,765,769,962,1009],{"language":16},[18,766,768],{"id":767},"q6-what-gets-logged-and-at-roughly-what-time","Q6. What gets logged, and at roughly what time?",[23,770,771],{"language":16},[26,772,774],{"className":28,"code":773,"language":16,"meta":30,"style":30},"function debounce(fn, wait) {\n  let timer;\n  return (...args) => {\n    clearTimeout(timer);\n    timer = setTimeout(() => fn(...args), wait);\n  };\n}\n\nconst log = debounce((x) => console.log(x), 200);\nlog(\"a\");\nsetTimeout(() => log(\"b\"), 100);\nsetTimeout(() => log(\"c\"), 250);\n",[32,775,776,796,803,823,831,855,860,864,869,903,914,939],{"__ignoreMap":30},[35,777,778,780,783,785,788,791,794],{"class":37,"line":38},[35,779,42],{"class":41},[35,781,782],{"class":45}," debounce",[35,784,72],{"class":49},[35,786,787],{"class":315},"fn",[35,789,790],{"class":49},", ",[35,792,793],{"class":315},"wait",[35,795,319],{"class":49},[35,797,798,800],{"class":37,"line":53},[35,799,85],{"class":41},[35,801,802],{"class":49}," timer;\n",[35,804,805,807,810,813,816,819,821],{"class":37,"line":82},[35,806,575],{"class":41},[35,808,809],{"class":49}," (",[35,811,812],{"class":41},"...",[35,814,815],{"class":315},"args",[35,817,818],{"class":49},") ",[35,820,117],{"class":41},[35,822,120],{"class":49},[35,824,825,828],{"class":37,"line":100},[35,826,827],{"class":45},"    clearTimeout",[35,829,830],{"class":49},"(timer);\n",[35,832,833,836,838,841,843,845,848,850,852],{"class":37,"line":123},[35,834,835],{"class":49},"    timer ",[35,837,91],{"class":41},[35,839,840],{"class":45}," setTimeout",[35,842,327],{"class":49},[35,844,117],{"class":41},[35,846,847],{"class":45}," fn",[35,849,72],{"class":49},[35,851,812],{"class":41},[35,853,854],{"class":49},"args), wait);\n",[35,856,857],{"class":37,"line":134},[35,858,859],{"class":49},"  };\n",[35,861,862],{"class":37,"line":146},[35,863,155],{"class":49},[35,865,866],{"class":37,"line":152},[35,867,868],{"emptyLinePlaceholder":198},"\n",[35,870,871,873,876,878,880,883,886,888,890,893,895,898,901],{"class":37,"line":158},[35,872,371],{"class":41},[35,874,875],{"class":59}," log",[35,877,63],{"class":41},[35,879,782],{"class":45},[35,881,882],{"class":49},"((",[35,884,885],{"class":315},"x",[35,887,818],{"class":49},[35,889,117],{"class":41},[35,891,892],{"class":49}," console.",[35,894,140],{"class":45},[35,896,897],{"class":49},"(x), ",[35,899,900],{"class":59},"200",[35,902,79],{"class":49},[35,904,905,907,909,912],{"class":37,"line":167},[35,906,140],{"class":45},[35,908,72],{"class":49},[35,910,911],{"class":75},"\"a\"",[35,913,79],{"class":49},[35,915,917,920,922,924,926,928,931,934,937],{"class":37,"line":916},11,[35,918,919],{"class":45},"setTimeout",[35,921,327],{"class":49},[35,923,117],{"class":41},[35,925,875],{"class":45},[35,927,72],{"class":49},[35,929,930],{"class":75},"\"b\"",[35,932,933],{"class":49},"), ",[35,935,936],{"class":59},"100",[35,938,79],{"class":49},[35,940,942,944,946,948,950,952,955,957,960],{"class":37,"line":941},12,[35,943,919],{"class":45},[35,945,327],{"class":49},[35,947,117],{"class":41},[35,949,875],{"class":45},[35,951,72],{"class":49},[35,953,954],{"class":75},"\"c\"",[35,956,933],{"class":49},[35,958,959],{"class":59},"250",[35,961,79],{"class":49},[186,963,965,981,990,998],{"className":964},[189],[191,966,968,262,970,790,973,976,977,980],{"className":967},[194],[196,969],{"disabled":198,"type":199},[32,971,972],{},"a",[32,974,975],{},"b",", and ",[32,978,979],{},"c"," all log, at 200ms, 300ms, and 450ms respectively — since each call schedules its own timer independently",[191,982,984,986,987,989],{"className":983},[194],[196,985],{"disabled":198,"type":199}," Only ",[32,988,979],{}," logs, at ~450ms — each new call clears the still-pending timer from the previous call before it can fire, and since no further call arrives within 200ms of the last one, only its timer survives to completion",[191,991,993,986,995,997],{"className":992},[194],[196,994],{"disabled":198,"type":199},[32,996,972],{}," logs, at 200ms — later calls are ignored once a timer is already pending",[191,999,1001,262,1003,1005,1006,1008],{"className":1000},[194],[196,1002],{"disabled":198,"type":199},[32,1004,975],{}," and ",[32,1007,979],{}," both log, at 300ms and 450ms — only the very first call's timer gets cancelled",[237,1010,1011,1013,1020],{},[240,1012,242],{},[244,1014,1015,1017,1018,989],{},[247,1016,249],{}," B — Only ",[32,1019,979],{},[244,1021,1022,262,1024,1026,1027,1030,1031,1034,1035,1038,1039,1041,1042,1045,1046,1048,1049,1052,1053,1055],{},[247,1023,261],{},[247,1025,265],{}," Trace it: ",[32,1028,1029],{},"log(\"a\")"," at t=0 schedules a fire at t=200. ",[32,1032,1033],{},"log(\"b\")"," at t=100 calls ",[32,1036,1037],{},"clearTimeout"," on that pending timer (cancelling ",[32,1040,972],{}," before it ever fires) and schedules its own fire at t=300. ",[32,1043,1044],{},"log(\"c\")"," at t=250 again clears the still-pending timer (cancelling ",[32,1047,975],{},", since 250 \u003C 300) and schedules its own fire at t=450. No further call arrives before t=450, so ",[32,1050,1051],{},"fn(\"c\")"," finally runs — nothing else does. Option A ignores that ",[32,1054,1037],{}," cancels the previous pending call entirely rather than letting each accumulate. Option C stops tracing too early. Option D misidentifies which timer survives.",[14,1057,1058,1067,1181,1221],{"language":16},[18,1059,1061,1062,1005,1064,1066],{"id":1060},"q7-what-perf-benefit-does-v8-get-from-a-and-b-being-built-with-the-same-property-order-and-types","Q7. What perf benefit does V8 get from ",[32,1063,972],{},[32,1065,975],{}," being built with the same property order and types?",[23,1068,1069],{"language":16},[26,1070,1072],{"className":28,"code":1071,"language":16,"meta":30,"style":30},"function makePoint(x, y) {\n  const p = {};\n  p.x = x;\n  p.y = y;\n  return p;\n}\nconst a = makePoint(1, 2);\nconst b = makePoint(3, 4);\n",[32,1073,1074,1092,1104,1114,1124,1131,1135,1158],{"__ignoreMap":30},[35,1075,1076,1078,1081,1083,1085,1087,1090],{"class":37,"line":38},[35,1077,42],{"class":41},[35,1079,1080],{"class":45}," makePoint",[35,1082,72],{"class":49},[35,1084,885],{"class":315},[35,1086,790],{"class":49},[35,1088,1089],{"class":315},"y",[35,1091,319],{"class":49},[35,1093,1094,1096,1099,1101],{"class":37,"line":53},[35,1095,56],{"class":41},[35,1097,1098],{"class":59}," p",[35,1100,63],{"class":41},[35,1102,1103],{"class":49}," {};\n",[35,1105,1106,1109,1111],{"class":37,"line":82},[35,1107,1108],{"class":49},"  p.x ",[35,1110,91],{"class":41},[35,1112,1113],{"class":49}," x;\n",[35,1115,1116,1119,1121],{"class":37,"line":100},[35,1117,1118],{"class":49},"  p.y ",[35,1120,91],{"class":41},[35,1122,1123],{"class":49}," y;\n",[35,1125,1126,1128],{"class":37,"line":123},[35,1127,575],{"class":41},[35,1129,1130],{"class":49}," p;\n",[35,1132,1133],{"class":37,"line":134},[35,1134,155],{"class":49},[35,1136,1137,1139,1142,1144,1146,1148,1151,1153,1156],{"class":37,"line":146},[35,1138,371],{"class":41},[35,1140,1141],{"class":59}," a",[35,1143,63],{"class":41},[35,1145,1080],{"class":45},[35,1147,72],{"class":49},[35,1149,1150],{"class":59},"1",[35,1152,790],{"class":49},[35,1154,1155],{"class":59},"2",[35,1157,79],{"class":49},[35,1159,1160,1162,1165,1167,1169,1171,1174,1176,1179],{"class":37,"line":152},[35,1161,371],{"class":41},[35,1163,1164],{"class":59}," b",[35,1166,63],{"class":41},[35,1168,1080],{"class":45},[35,1170,72],{"class":49},[35,1172,1173],{"class":59},"3",[35,1175,790],{"class":49},[35,1177,1178],{"class":59},"4",[35,1180,79],{"class":49},[186,1182,1184,1190,1201,1211],{"className":1183},[189],[191,1185,1187,1189],{"className":1186},[194],[196,1188],{"disabled":198,"type":199}," Nothing — V8 treats every object as a fully dynamic hash map regardless of creation pattern",[191,1191,1193,1195,1196,1005,1198,1200],{"className":1192},[194],[196,1194],{"disabled":198,"type":199}," V8 merges ",[32,1197,972],{},[32,1199,975],{}," into a single object in memory to save space",[191,1202,1204,262,1206,1005,1208,1210],{"className":1203},[194],[196,1205],{"disabled":198,"type":199},[32,1207,972],{},[32,1209,975],{}," end up sharing the same underlying \"hidden class\" (shape), letting V8 use fast, offset-based property access instead of a slower dictionary lookup for both objects",[191,1212,1214,1216,1217,1220],{"className":1213},[194],[196,1215],{"disabled":198,"type":199}," Hidden classes only apply to objects created with ",[32,1218,1219],{},"class"," syntax, not object literals",[237,1222,1223,1225,1234],{},[240,1224,242],{},[244,1226,1227,1229,1230,1005,1232,1210],{},[247,1228,249],{}," C — ",[32,1231,972],{},[32,1233,975],{},[244,1235,1236,262,1238,1241,1242,1245,1246,1248,1249,1251,1252,1005,1254,1256,1257,1259],{},[247,1237,261],{},[247,1239,1240],{},"Performance:"," V8 assigns objects an internal \"hidden class\" (a.k.a. \"shape\" or \"map\") that describes their property layout. Because ",[32,1243,1244],{},"makePoint"," always adds ",[32,1247,885],{}," then ",[32,1250,1089],{},", in that order, with consistent value types, both ",[32,1253,972],{},[32,1255,975],{}," transition through the exact same sequence of hidden classes and end up sharing one — letting V8 compile property access as a fixed memory offset instead of a hash-map lookup. Option A describes the slow, generic fallback path that hidden classes exist to avoid. Option B fabricates object merging. Option D is false — hidden classes apply to any object, ",[32,1258,1219],{},"-based or not.",[14,1261,1262,1266,1315,1368],{"language":16},[18,1263,1265],{"id":1264},"q8-building-on-the-previous-scenario-what-happens-to-the-shared-hidden-class-optimization-once-this-runs","Q8. Building on the previous scenario, what happens to the shared hidden-class optimization once this runs?",[23,1267,1268],{"language":16},[26,1269,1271],{"className":28,"code":1270,"language":16,"meta":30,"style":30},"b.z = 99;\nif (Math.random() > 0.9) a.y = \"surprise\";\n",[32,1272,1273,1285],{"__ignoreMap":30},[35,1274,1275,1278,1280,1283],{"class":37,"line":38},[35,1276,1277],{"class":49},"b.z ",[35,1279,91],{"class":41},[35,1281,1282],{"class":59}," 99",[35,1284,97],{"class":49},[35,1286,1287,1290,1293,1296,1299,1302,1305,1308,1310,1313],{"class":37,"line":53},[35,1288,1289],{"class":41},"if",[35,1291,1292],{"class":49}," (Math.",[35,1294,1295],{"class":45},"random",[35,1297,1298],{"class":49},"() ",[35,1300,1301],{"class":41},">",[35,1303,1304],{"class":59}," 0.9",[35,1306,1307],{"class":49},") a.y ",[35,1309,91],{"class":41},[35,1311,1312],{"class":75}," \"surprise\"",[35,1314,97],{"class":49},[186,1316,1318,1324,1337,1343],{"className":1317},[189],[191,1319,1321,1323],{"className":1320},[194],[196,1322],{"disabled":198,"type":199}," Nothing changes — hidden classes are assigned once at creation and never affected by later mutation",[191,1325,1327,1329,1330,1333,1334,1336],{"className":1326},[194],[196,1328],{"disabled":198,"type":199}," V8 automatically adds a ",[32,1331,1332],{},"z"," property to ",[32,1335,972],{}," as well to keep the shapes in sync",[191,1338,1340,1342],{"className":1339},[194],[196,1341],{"disabled":198,"type":199}," This only matters for arrays, not plain objects",[191,1344,1346,1348,1349,1351,1352,1354,1355,1357,1358,1360,1361,1364,1365,1367],{"className":1345},[194],[196,1347],{"disabled":198,"type":199}," Adding ",[32,1350,1332],{}," only to ",[32,1353,975],{}," gives ",[32,1356,975],{}," a different hidden-class transition than ",[32,1359,972],{},", and changing ",[32,1362,1363],{},"a.y"," from a number to a string can force V8 to fall back to a slower representation for ",[32,1366,972],{}," — both objects lose the shared fast shape, and functions operating on both become polymorphic",[237,1369,1370,1372,1389],{},[240,1371,242],{},[244,1373,1374,1376,1377,1351,1379,1354,1381,1357,1383,1360,1385,1364,1387,1367],{},[247,1375,249],{}," D — Adding ",[32,1378,1332],{},[32,1380,975],{},[32,1382,975],{},[32,1384,972],{},[32,1386,1363],{},[32,1388,972],{},[244,1390,1391,262,1393,1395,1396,1351,1398,1400,1401,1403,1404,1406,1407,1409,1410,1412],{},[247,1392,261],{},[247,1394,1240],{}," Hidden classes are dynamic, not frozen at creation — every property addition or type change is itself a transition to a (possibly new) hidden class. Adding ",[32,1397,1332],{},[32,1399,975],{}," diverges ",[32,1402,975],{},"'s shape from ",[32,1405,972],{},"'s; changing ",[32,1408,1363],{},"'s type mid-flight can force V8 out of the fast tracked-shape path entirely for ",[32,1411,972],{},". Any function that previously saw only one shared shape now sees two different shapes, becoming polymorphic and losing its fast inline-cached path. Option A is false — mutation absolutely affects hidden-class assignment. Option B invents synchronization behavior V8 doesn't perform. Option C is wrong — this applies to any plain object, not just arrays.",[14,1414,1415,1431,1468,1498],{"language":16},[18,1416,1418,1419,1422,1423,1426,1427,1430],{"id":1417},"q9-getareashape-reads-shapewidth-shapeheight-and-runs-thousands-of-times-per-frame-why-is-it-faster-when-every-shape-has-the-same-property-layout-than-when-shapes-vary","Q9. ",[32,1420,1421],{},"getArea(shape)"," reads ",[32,1424,1425],{},"shape.width * shape.height"," and runs thousands of times per frame. Why is it faster when every ",[32,1428,1429],{},"shape"," has the same property layout than when shapes vary?",[23,1432,1433],{"language":16},[26,1434,1436],{"className":28,"code":1435,"language":16,"meta":30,"style":30},"function getArea(shape) {\n  return shape.width * shape.height;\n}\n",[32,1437,1438,1451,1464],{"__ignoreMap":30},[35,1439,1440,1442,1445,1447,1449],{"class":37,"line":38},[35,1441,42],{"class":41},[35,1443,1444],{"class":45}," getArea",[35,1446,72],{"class":49},[35,1448,1429],{"class":315},[35,1450,319],{"class":49},[35,1452,1453,1455,1458,1461],{"class":37,"line":53},[35,1454,575],{"class":41},[35,1456,1457],{"class":49}," shape.width ",[35,1459,1460],{"class":41},"*",[35,1462,1463],{"class":49}," shape.height;\n",[35,1465,1466],{"class":37,"line":82},[35,1467,155],{"class":49},[186,1469,1471,1477,1483,1492],{"className":1470},[189],[191,1472,1474,1476],{"className":1473},[194],[196,1475],{"disabled":198,"type":199}," Consistent shapes make the call site monomorphic (one hidden class seen), letting the JIT inline a fast, specialized path; varying shapes make it polymorphic\u002Fmegamorphic, forcing V8 to fall back to slower generic property lookups and inline-cache misses",[191,1478,1480,1482],{"className":1479},[194],[196,1481],{"disabled":198,"type":199}," It doesn't matter — the JIT re-optimizes on every call regardless of shape",[191,1484,1486,986,1488,1491],{"className":1485},[194],[196,1487],{"disabled":198,"type":199},[32,1489,1490],{},"TypedArray","s benefit from consistent shapes; plain objects are unaffected",[191,1493,1495,1497],{"className":1494},[194],[196,1496],{"disabled":198,"type":199}," Polymorphic call sites are always faster because V8 can choose the best strategy per call",[237,1499,1500,1502,1507],{},[240,1501,242],{},[244,1503,1504,1506],{},[247,1505,249],{}," A — Consistent shapes make the call site monomorphic (one hidden class seen), letting the JIT inline a fast, specialized path; varying shapes make it polymorphic\u002Fmegamorphic, forcing V8 to fall back to slower generic property lookups and inline-cache misses",[244,1508,1509,262,1511,1513,1514,1516,1517,1520,1521,1524,1525,1528],{},[247,1510,261],{},[247,1512,1240],{}," V8's inline caches record which hidden class(es) a call site has seen for ",[32,1515,1429],{},". If it's always the same shape (monomorphic), the JIT can specialize ",[32,1518,1519],{},"getArea"," to read ",[32,1522,1523],{},"width","\u002F",[32,1526,1527],{},"height"," at fixed offsets directly. Once the call site sees several distinct shapes (polymorphic) or too many (megamorphic), V8 gives up on a specialized fast path and falls back to a generic, slower lookup on every call. Option B and D contradict how inline caches actually behave — variety is a cost, not a benefit. Option C is false; this optimization applies broadly to plain objects, not just typed arrays.",[14,1530,1531,1539,1601,1648],{"language":16},[18,1532,1534,1535,1538],{"id":1533},"q10-in-a-hot-path-whats-the-perf-concern-with-delete-cfgdebug-and-whats-the-preferred-alternative","Q10. In a hot path, what's the perf concern with ",[32,1536,1537],{},"delete cfg.debug",", and what's the preferred alternative?",[23,1540,1541],{"language":16},[26,1542,1544],{"className":28,"code":1543,"language":16,"meta":30,"style":30},"const config = { debug: true, retries: 3, timeout: 500 };\nfunction disableDebug(cfg) {\n  delete cfg.debug;\n}\n",[32,1545,1546,1575,1589,1597],{"__ignoreMap":30},[35,1547,1548,1550,1553,1555,1558,1561,1564,1566,1569,1572],{"class":37,"line":38},[35,1549,371],{"class":41},[35,1551,1552],{"class":59}," config",[35,1554,63],{"class":41},[35,1556,1557],{"class":49}," { debug: ",[35,1559,1560],{"class":59},"true",[35,1562,1563],{"class":49},", retries: ",[35,1565,1173],{"class":59},[35,1567,1568],{"class":49},", timeout: ",[35,1570,1571],{"class":59},"500",[35,1573,1574],{"class":49}," };\n",[35,1576,1577,1579,1582,1584,1587],{"class":37,"line":53},[35,1578,42],{"class":41},[35,1580,1581],{"class":45}," disableDebug",[35,1583,72],{"class":49},[35,1585,1586],{"class":315},"cfg",[35,1588,319],{"class":49},[35,1590,1591,1594],{"class":37,"line":82},[35,1592,1593],{"class":41},"  delete",[35,1595,1596],{"class":49}," cfg.debug;\n",[35,1598,1599],{"class":37,"line":100},[35,1600,155],{"class":49},[186,1602,1604,1613,1625,1637],{"className":1603},[189],[191,1605,1607,262,1609,1612],{"className":1606},[194],[196,1608],{"disabled":198,"type":199},[32,1610,1611],{},"delete"," is always fastest since it fully frees the property's memory immediately",[191,1614,1616,262,1618,1620,1621,1624],{"className":1615},[194],[196,1617],{"disabled":198,"type":199},[32,1619,1611],{}," changes the object's hidden class (creating a new, often \"dictionary mode\" shape), which can deoptimize property access for that object; setting ",[32,1622,1623],{},"cfg.debug = undefined"," (or restructuring so the property was never conditionally needed) preserves the original shape and avoids the transition",[191,1626,1628,1630,1631,1633,1634],{"className":1627},[194],[196,1629],{"disabled":198,"type":199}," There's no concern — ",[32,1632,1611],{}," behaves identically to setting a property to ",[32,1635,1636],{},"undefined",[191,1638,1640,262,1642,1644,1645],{"className":1639},[194],[196,1641],{"disabled":198,"type":199},[32,1643,1611],{}," is disallowed in strict mode, so this code throws a ",[32,1646,1647],{},"SyntaxError",[237,1649,1650,1652,1661],{},[240,1651,242],{},[244,1653,1654,1656,1657,1620,1659,1624],{},[247,1655,249],{}," B — ",[32,1658,1611],{},[32,1660,1623],{},[244,1662,1663,262,1665,1667,1668,1670,1671,1673,1674,1676],{},[247,1664,261],{},[247,1666,1240],{}," Unlike simply overwriting a value, ",[32,1669,1611],{}," removes a property slot entirely, which forces a hidden-class transition and can push the object into a slower, dictionary-mode (hash-map-like) representation that loses the fast offset-based access other objects of the \"same\" original shape still enjoy. ",[32,1672,1623],{}," keeps the property (and the object's shape) intact — only the value changes — so it avoids the deopt. Option A gets the performance direction backwards. Option C ignores the shape-transition cost that makes the two meaningfully different. Option D is fabricated — ",[32,1675,1611],{}," on a configurable own property is valid in strict mode.",[14,1678,1679,1695,1824,1870],{"language":16},[18,1680,1682,1683,1686,1687,1690,1691,1694],{"id":1681},"q11-why-can-iteratingsumming-dense-be-significantly-faster-than-doing-the-same-over-sparse-or-alsosparse","Q11. Why can iterating\u002Fsumming ",[32,1684,1685],{},"dense"," be significantly faster than doing the same over ",[32,1688,1689],{},"sparse"," or ",[32,1692,1693],{},"alsoSparse","?",[23,1696,1697],{"language":16},[26,1698,1700],{"className":28,"code":1699,"language":16,"meta":30,"style":30},"const dense = [1, 2, 3, 4, 5];\n\nconst sparse = [1, 2, 3, 4, 5];\ndelete sparse[2];\n\nconst alsoSparse = new Array(5);\nalsoSparse[0] = \"x\";\n",[32,1701,1702,1736,1740,1771,1782,1786,1806],{"__ignoreMap":30},[35,1703,1704,1706,1709,1711,1714,1716,1718,1720,1722,1724,1726,1728,1730,1733],{"class":37,"line":38},[35,1705,371],{"class":41},[35,1707,1708],{"class":59}," dense",[35,1710,63],{"class":41},[35,1712,1713],{"class":49}," [",[35,1715,1150],{"class":59},[35,1717,790],{"class":49},[35,1719,1155],{"class":59},[35,1721,790],{"class":49},[35,1723,1173],{"class":59},[35,1725,790],{"class":49},[35,1727,1178],{"class":59},[35,1729,790],{"class":49},[35,1731,1732],{"class":59},"5",[35,1734,1735],{"class":49},"];\n",[35,1737,1738],{"class":37,"line":53},[35,1739,868],{"emptyLinePlaceholder":198},[35,1741,1742,1744,1747,1749,1751,1753,1755,1757,1759,1761,1763,1765,1767,1769],{"class":37,"line":82},[35,1743,371],{"class":41},[35,1745,1746],{"class":59}," sparse",[35,1748,63],{"class":41},[35,1750,1713],{"class":49},[35,1752,1150],{"class":59},[35,1754,790],{"class":49},[35,1756,1155],{"class":59},[35,1758,790],{"class":49},[35,1760,1173],{"class":59},[35,1762,790],{"class":49},[35,1764,1178],{"class":59},[35,1766,790],{"class":49},[35,1768,1732],{"class":59},[35,1770,1735],{"class":49},[35,1772,1773,1775,1778,1780],{"class":37,"line":100},[35,1774,1611],{"class":41},[35,1776,1777],{"class":49}," sparse[",[35,1779,1155],{"class":59},[35,1781,1735],{"class":49},[35,1783,1784],{"class":37,"line":123},[35,1785,868],{"emptyLinePlaceholder":198},[35,1787,1788,1790,1793,1795,1797,1800,1802,1804],{"class":37,"line":134},[35,1789,371],{"class":41},[35,1791,1792],{"class":59}," alsoSparse",[35,1794,63],{"class":41},[35,1796,341],{"class":41},[35,1798,1799],{"class":45}," Array",[35,1801,72],{"class":49},[35,1803,1732],{"class":59},[35,1805,79],{"class":49},[35,1807,1808,1811,1814,1817,1819,1822],{"class":37,"line":146},[35,1809,1810],{"class":49},"alsoSparse[",[35,1812,1813],{"class":59},"0",[35,1815,1816],{"class":49},"] ",[35,1818,91],{"class":41},[35,1820,1821],{"class":75}," \"x\"",[35,1823,97],{"class":49},[186,1825,1827,1833,1843,1857],{"className":1826},[189],[191,1828,1830,1832],{"className":1829},[194],[196,1831],{"disabled":198,"type":199}," There is no difference — all three are stored identically as contiguous memory blocks",[191,1834,1836,262,1838,1005,1840,1842],{"className":1835},[194],[196,1837],{"disabled":198,"type":199},[32,1839,1689],{},[32,1841,1693],{}," are actually faster because V8 pre-allocates extra memory for them",[191,1844,1846,262,1848,1850,1851,1690,1853,1856],{"className":1845},[194],[196,1847],{"disabled":198,"type":199},[32,1849,1685],{}," stays on V8's fast, packed \"elements kind\" (contiguous backing store); creating holes with ",[32,1852,1611],{},[32,1854,1855],{},"new Array(n)"," transitions the array to a slower \"holey\"\u002Fdictionary-like representation, since engines must now check for and skip missing indices on every access",[191,1858,1860,262,1862,1865,1866,1869],{"className":1859},[194],[196,1861],{"disabled":198,"type":199},[32,1863,1864],{},"delete sparse[2]"," shrinks the array's ",[32,1867,1868],{},"length"," by one, making it faster to iterate",[237,1871,1872,1874,1884],{},[240,1873,242],{},[244,1875,1876,1229,1878,1850,1880,1690,1882,1856],{},[247,1877,249],{},[32,1879,1685],{},[32,1881,1611],{},[32,1883,1855],{},[244,1885,1886,262,1888,1890,1891,1893,1894,1896,1897,1899,1900,1903,1904,1906,1907,1909],{},[247,1887,261],{},[247,1889,1240],{}," V8 tracks an array's \"elements kind\" internally. A fully packed array like ",[32,1892,1685],{}," can use a tight, contiguous fast path. ",[32,1895,1864],{}," doesn't shrink the array or fill the gap — it leaves an actual hole at index 2 (the array's ",[32,1898,1868],{}," stays 5), which downgrades it to a \"holey\" representation that must check for missing slots on every access. ",[32,1901,1902],{},"new Array(5)"," starts out entirely holey by construction, for the same reason. Option A and B invert the real cost. Option D is a common misconception — ",[32,1905,1611],{}," on an array index never changes ",[32,1908,1868],{},".",[14,1911,1912,1924,1963],{},[18,1913,1915,1916,1919,1920,1923],{"id":1914},"q12-an-animation-loop-uses-setintervalupdate-16-instead-of-requestanimationframeupdate-beyond-potential-drift-from-the-displays-refresh-rate-whats-another-concrete-downside-in-production","Q12. An animation loop uses ",[32,1917,1918],{},"setInterval(update, 16)"," instead of ",[32,1921,1922],{},"requestAnimationFrame(update)",". Beyond potential drift from the display's refresh rate, what's another concrete downside in production?",[186,1925,1927,1935,1943,1951],{"className":1926},[189],[191,1928,1930,262,1932,1934],{"className":1929},[194],[196,1931],{"disabled":198,"type":199},[32,1933,438],{}," cannot be cleared once started, so the animation runs forever even after the component unmounts",[191,1936,1938,262,1940,1942],{"className":1937},[194],[196,1939],{"disabled":198,"type":199},[32,1941,438],{}," runs on a separate thread, causing race conditions with the DOM",[191,1944,1946,262,1948,1950],{"className":1945},[194],[196,1947],{"disabled":198,"type":199},[32,1949,438],{}," cannot accept a callback that touches the DOM",[191,1952,1954,262,1956,1958,1959,1962],{"className":1953},[194],[196,1955],{"disabled":198,"type":199},[32,1957,438],{}," keeps firing at roughly the same rate even when the tab is backgrounded\u002Fhidden, wasting CPU and battery on invisible work, whereas ",[32,1960,1961],{},"requestAnimationFrame"," callbacks are automatically throttled\u002Fpaused by the browser for hidden tabs",[237,1964,1965,1967,1976],{},[240,1966,242],{},[244,1968,1969,1971,1972,1958,1974,1962],{},[247,1970,249],{}," D — ",[32,1973,438],{},[32,1975,1961],{},[244,1977,1978,262,1980,1982,1983,1985,1986,1988,1989,1991,1992,1994,1995,1909],{},[247,1979,261],{},[247,1981,1240],{}," Browsers specifically de-prioritize ",[32,1984,1961],{}," for hidden\u002Fbackground tabs (heavily throttling or pausing it entirely, since there's nothing to paint), which saves CPU and battery for work the user can't even see. ",[32,1987,438],{}," has no such built-in awareness of visibility and keeps ticking at roughly its configured rate regardless, silently burning resources in the background. Option A is false — ",[32,1990,465],{}," works identically to clearing any interval. Options B and C invent thread and DOM-access restrictions that don't exist; ",[32,1993,438],{}," runs on the main thread just like ",[32,1996,1961],{},[14,1998,1999,2003,2243,2282],{"language":16},[18,2000,2002],{"id":2001},"q13-running-this-synchronous-computation-in-a-click-handler-freezes-the-ui-for-its-duration-whats-the-correct-fix-and-why","Q13. Running this synchronous computation in a click handler freezes the UI for its duration. What's the correct fix, and why?",[23,2004,2005],{"language":16},[26,2006,2008],{"className":28,"code":2007,"language":16,"meta":30,"style":30},"function findPrimesUpTo(n) {\n  const primes = [];\n  for (let i = 2; i \u003C= n; i++) {\n    let isPrime = true;\n    for (let j = 2; j * j \u003C= i; j++) {\n      if (i % j === 0) { isPrime = false; break; }\n    }\n    if (isPrime) primes.push(i);\n  }\n  return primes;\n}\ndocument.getElementById(\"go\").addEventListener(\"click\", () => {\n  const result = findPrimesUpTo(5_000_000);\n  render(result);\n});\n",[32,2009,2010,2024,2036,2067,2082,2114,2149,2154,2168,2173,2180,2184,2209,2228,2237],{"__ignoreMap":30},[35,2011,2012,2014,2017,2019,2022],{"class":37,"line":38},[35,2013,42],{"class":41},[35,2015,2016],{"class":45}," findPrimesUpTo",[35,2018,72],{"class":49},[35,2020,2021],{"class":315},"n",[35,2023,319],{"class":49},[35,2025,2026,2028,2031,2033],{"class":37,"line":53},[35,2027,56],{"class":41},[35,2029,2030],{"class":59}," primes",[35,2032,63],{"class":41},[35,2034,2035],{"class":49}," [];\n",[35,2037,2038,2041,2043,2046,2049,2051,2054,2057,2060,2063,2065],{"class":37,"line":82},[35,2039,2040],{"class":41},"  for",[35,2042,809],{"class":49},[35,2044,2045],{"class":41},"let",[35,2047,2048],{"class":49}," i ",[35,2050,91],{"class":41},[35,2052,2053],{"class":59}," 2",[35,2055,2056],{"class":49},"; i ",[35,2058,2059],{"class":41},"\u003C=",[35,2061,2062],{"class":49}," n; i",[35,2064,129],{"class":41},[35,2066,319],{"class":49},[35,2068,2069,2072,2075,2077,2080],{"class":37,"line":100},[35,2070,2071],{"class":41},"    let",[35,2073,2074],{"class":49}," isPrime ",[35,2076,91],{"class":41},[35,2078,2079],{"class":59}," true",[35,2081,97],{"class":49},[35,2083,2084,2087,2089,2091,2094,2096,2098,2101,2103,2105,2107,2110,2112],{"class":37,"line":123},[35,2085,2086],{"class":41},"    for",[35,2088,809],{"class":49},[35,2090,2045],{"class":41},[35,2092,2093],{"class":49}," j ",[35,2095,91],{"class":41},[35,2097,2053],{"class":59},[35,2099,2100],{"class":49},"; j ",[35,2102,1460],{"class":41},[35,2104,2093],{"class":49},[35,2106,2059],{"class":41},[35,2108,2109],{"class":49}," i; j",[35,2111,129],{"class":41},[35,2113,319],{"class":49},[35,2115,2116,2119,2122,2125,2127,2130,2132,2135,2137,2140,2143,2146],{"class":37,"line":134},[35,2117,2118],{"class":41},"      if",[35,2120,2121],{"class":49}," (i ",[35,2123,2124],{"class":41},"%",[35,2126,2093],{"class":49},[35,2128,2129],{"class":41},"===",[35,2131,94],{"class":59},[35,2133,2134],{"class":49},") { isPrime ",[35,2136,91],{"class":41},[35,2138,2139],{"class":59}," false",[35,2141,2142],{"class":49},"; ",[35,2144,2145],{"class":41},"break",[35,2147,2148],{"class":49},"; }\n",[35,2150,2151],{"class":37,"line":146},[35,2152,2153],{"class":49},"    }\n",[35,2155,2156,2159,2162,2165],{"class":37,"line":152},[35,2157,2158],{"class":41},"    if",[35,2160,2161],{"class":49}," (isPrime) primes.",[35,2163,2164],{"class":45},"push",[35,2166,2167],{"class":49},"(i);\n",[35,2169,2170],{"class":37,"line":158},[35,2171,2172],{"class":49},"  }\n",[35,2174,2175,2177],{"class":37,"line":167},[35,2176,575],{"class":41},[35,2178,2179],{"class":49}," primes;\n",[35,2181,2182],{"class":37,"line":916},[35,2183,155],{"class":49},[35,2185,2186,2188,2190,2192,2195,2197,2199,2201,2203,2205,2207],{"class":37,"line":941},[35,2187,170],{"class":49},[35,2189,69],{"class":45},[35,2191,72],{"class":49},[35,2193,2194],{"class":75},"\"go\"",[35,2196,179],{"class":49},[35,2198,106],{"class":45},[35,2200,72],{"class":49},[35,2202,111],{"class":75},[35,2204,114],{"class":49},[35,2206,117],{"class":41},[35,2208,120],{"class":49},[35,2210,2212,2214,2217,2219,2221,2223,2226],{"class":37,"line":2211},13,[35,2213,56],{"class":41},[35,2215,2216],{"class":59}," result",[35,2218,63],{"class":41},[35,2220,2016],{"class":45},[35,2222,72],{"class":49},[35,2224,2225],{"class":59},"5_000_000",[35,2227,79],{"class":49},[35,2229,2231,2234],{"class":37,"line":2230},14,[35,2232,2233],{"class":45},"  render",[35,2235,2236],{"class":49},"(result);\n",[35,2238,2240],{"class":37,"line":2239},15,[35,2241,2242],{"class":49},"});\n",[186,2244,2246,2252,2262,2272],{"className":2245},[189],[191,2247,2249,2251],{"className":2248},[194],[196,2250],{"disabled":198,"type":199}," Move the computation into a Web Worker — since JS is single-threaded, any synchronous CPU-bound work on the main thread blocks rendering and event handling no matter how it's scheduled; a worker runs on a separate thread so the main thread stays responsive",[191,2253,2255,2257,2258,2261],{"className":2254},[194],[196,2256],{"disabled":198,"type":199}," Wrap the call in a ",[32,2259,2260],{},"Promise"," so it runs asynchronously without blocking",[191,2263,2265,2267,2268,2271],{"className":2264},[194],[196,2266],{"disabled":198,"type":199}," Use ",[32,2269,2270],{},"setTimeout(() => findPrimesUpTo(5_000_000), 0)"," to defer it to a macrotask",[191,2273,2275,2277,2278,2281],{"className":2274},[194],[196,2276],{"disabled":198,"type":199}," Mark the function ",[32,2279,2280],{},"async"," so it yields control back to the event loop periodically",[237,2283,2284,2286,2291],{},[240,2285,242],{},[244,2287,2288,2290],{},[247,2289,249],{}," A — Move the computation into a Web Worker — since JS is single-threaded, any synchronous CPU-bound work on the main thread blocks rendering and event handling no matter how it's scheduled; a worker runs on a separate thread so the main thread stays responsive",[244,2292,2293,262,2295,2297,2298,2300,2301,2304,2305,2307,2308,2311,2312,2314],{},[247,2294,261],{},[247,2296,1240],{}," JavaScript on the main thread is single-threaded and cooperative — once a synchronous function starts running, it monopolizes the thread (blocking layout, painting, and input handling) until it returns, no matter how it got invoked. A Web Worker executes on an entirely separate thread, so the loop runs without ever touching the thread responsible for rendering and interaction. Option B is wrong — wrapping in a ",[32,2299,2260],{}," doesn't make the executor's synchronous body non-blocking; the loop still runs to completion inline. Option C only delays ",[679,2302,2303],{},"when"," the freeze starts, not whether it happens. Option D is wrong — ",[32,2306,2280],{}," functions don't magically yield mid-loop; only an actual ",[32,2309,2310],{},"await"," (or moving off-thread) does, and there's no ",[32,2313,2310],{}," anywhere in this loop.",[14,2316,2317,2321,2360],{},[18,2318,2320],{"id":2319},"q14-a-single-page-app-ships-one-3mb-js-bundle-containing-code-for-every-route-including-an-admin-dashboard-only-2-of-users-ever-visit-whats-the-standard-fix-and-what-does-it-primarily-improve","Q14. A single-page app ships one 3MB JS bundle containing code for every route, including an admin dashboard only 2% of users ever visit. What's the standard fix, and what does it primarily improve?",[186,2322,2324,2330,2336,2346],{"className":2323},[189],[191,2325,2327,2329],{"className":2326},[194],[196,2328],{"disabled":198,"type":199}," Minify the bundle further — minification alone can typically cut a well-structured bundle to under 3MB total",[191,2331,2333,2335],{"className":2332},[194],[196,2334],{"disabled":198,"type":199}," Split the bundle by route\u002Ffeature (code splitting) and lazy-load the admin dashboard's code only when a user navigates to it — this reduces the initial bundle size, improving time-to-interactive for the 98% of users who never load that code",[191,2337,2339,2341,2342,2345],{"className":2338},[194],[196,2340],{"disabled":198,"type":199}," Inline all JS into the HTML ",[32,2343,2344],{},"\u003Chead>"," so there's no separate network request",[191,2347,2349,2351,2352,2355,2356,2359],{"className":2348},[194],[196,2350],{"disabled":198,"type":199}," Move all JS to inline ",[32,2353,2354],{},"\u003Cscript>"," tags at the bottom of ",[32,2357,2358],{},"\u003Cbody>"," instead of an external file",[237,2361,2362,2364,2369],{},[240,2363,242],{},[244,2365,2366,2368],{},[247,2367,249],{}," B — Split the bundle by route\u002Ffeature (code splitting) and lazy-load the admin dashboard's code only when a user navigates to it — this reduces the initial bundle size, improving time-to-interactive for the 98% of users who never load that code",[244,2370,2371,2373,2374,2377,2378,2380],{},[247,2372,261],{}," Code splitting plus lazy loading (e.g., dynamic ",[32,2375,2376],{},"import()",") lets the bundler emit separate chunks per route\u002Ffeature, so the browser downloads and parses only what the current page needs, shrinking the critical bundle most users actually pay for. Option A overstates what minification alone can realistically achieve on an already-built, feature-complete bundle. Options C and D just relocate the same amount of JS without reducing what has to be downloaded and parsed before the page becomes interactive — they can make things worse by blocking ",[32,2379,2344],{}," parsing or bloating the HTML document itself.",[14,2382,2383,2391,2433],{},[18,2384,2386,2387,2390],{"id":2385},"q15-a-page-renders-a-scrollable-table-of-50000-rows-by-mapping-the-full-dataset-directly-to-50000-tr-elements-causing-visible-jank-whats-the-standard-fix-and-why-does-it-help","Q15. A page renders a scrollable table of 50,000 rows by mapping the full dataset directly to 50,000 ",[32,2388,2389],{},"\u003Ctr>"," elements, causing visible jank. What's the standard fix, and why does it help?",[186,2392,2394,2408,2418,2424],{"className":2393},[189],[191,2395,2397,2399,2400,2403,2404,2407],{"className":2396},[194],[196,2398],{"disabled":198,"type":199}," Replace the ",[32,2401,2402],{},"\u003Ctable>"," with ",[32,2405,2406],{},"\u003Cdiv>","s, since divs render faster than table elements",[191,2409,2411,2413,2414,2417],{"className":2410},[194],[196,2412],{"disabled":198,"type":199}," Add ",[32,2415,2416],{},"will-change: transform"," to every row to force GPU acceleration of all 50,000 rows",[191,2419,2421,2423],{"className":2420},[194],[196,2422],{"disabled":198,"type":199}," Use virtualization\u002Fwindowing to render only the rows currently visible in the viewport (plus a small buffer), swapping their content as the user scrolls — this keeps the number of live DOM nodes small regardless of dataset size, avoiding the cost of creating\u002Flaying out\u002Fpainting tens of thousands of nodes at once",[191,2425,2427,2267,2429,2432],{"className":2426},[194],[196,2428],{"disabled":198,"type":199},[32,2430,2431],{},"list-style: none"," to skip the browser's default list rendering",[237,2434,2435,2437,2442],{},[240,2436,242],{},[244,2438,2439,2441],{},[247,2440,249],{}," C — Use virtualization\u002Fwindowing to render only the rows currently visible in the viewport (plus a small buffer), swapping their content as the user scrolls — this keeps the number of live DOM nodes small regardless of dataset size, avoiding the cost of creating\u002Flaying out\u002Fpainting tens of thousands of nodes at once",[244,2443,2444,2446,2447,2450,2451,1524,2453,2455],{},[247,2445,261],{}," Virtualization decouples the number of live DOM nodes from the size of the underlying dataset — only the handful of rows actually in (or near) the viewport ever exist as real elements, with the rest represented purely as data until they scroll into view. This keeps DOM node count, layout cost, and paint cost roughly constant no matter how large the dataset grows. Option A is a myth — element tag choice has negligible impact next to node count. Option B actually makes things worse, since ",[32,2448,2449],{},"will-change"," reserves GPU compositing layers per element and applying it to 50,000 rows wastes memory. Option D is irrelevant to ",[32,2452,2402],{},[32,2454,2389],{}," rendering, which has no list markers to begin with.",[14,2457,2458,2462,2533,2581],{"language":16},[18,2459,2461],{"id":2460},"q16-a-particle-based-game-loop-allocates-thousands-of-new-particle-objects-per-second-and-discards-them-once-off-screen-running-at-60fps-players-notice-periodic-stutters-whats-a-common-cause-and-fix","Q16. A particle-based game loop allocates thousands of new particle objects per second and discards them once off-screen, running at 60fps. Players notice periodic stutters. What's a common cause and fix?",[23,2463,2464],{"language":16},[26,2465,2467],{"className":28,"code":2466,"language":16,"meta":30,"style":30},"function spawnParticles(n) {\n  for (let i = 0; i \u003C n; i++) {\n    particles.push(new Particle(x, y, vx, vy));\n  }\n}\n",[32,2468,2469,2482,2507,2525,2529],{"__ignoreMap":30},[35,2470,2471,2473,2476,2478,2480],{"class":37,"line":38},[35,2472,42],{"class":41},[35,2474,2475],{"class":45}," spawnParticles",[35,2477,72],{"class":49},[35,2479,2021],{"class":315},[35,2481,319],{"class":49},[35,2483,2484,2486,2488,2490,2492,2494,2496,2498,2501,2503,2505],{"class":37,"line":53},[35,2485,2040],{"class":41},[35,2487,809],{"class":49},[35,2489,2045],{"class":41},[35,2491,2048],{"class":49},[35,2493,91],{"class":41},[35,2495,94],{"class":59},[35,2497,2056],{"class":49},[35,2499,2500],{"class":41},"\u003C",[35,2502,2062],{"class":49},[35,2504,129],{"class":41},[35,2506,319],{"class":49},[35,2508,2509,2512,2514,2516,2519,2522],{"class":37,"line":82},[35,2510,2511],{"class":49},"    particles.",[35,2513,2164],{"class":45},[35,2515,72],{"class":49},[35,2517,2518],{"class":41},"new",[35,2520,2521],{"class":45}," Particle",[35,2523,2524],{"class":49},"(x, y, vx, vy));\n",[35,2526,2527],{"class":37,"line":100},[35,2528,2172],{"class":49},[35,2530,2531],{"class":37,"line":123},[35,2532,155],{"class":49},[186,2534,2536,2548,2560,2575],{"className":2535},[189],[191,2537,2539,2541,2542,2544,2545,2547],{"className":2538},[194],[196,2540],{"disabled":198,"type":199}," The stutters are caused by ",[32,2543,1961],{}," itself pausing periodically; switching to ",[32,2546,438],{}," fixes it",[191,2549,2551,2553,2554,2556,2557],{"className":2550},[194],[196,2552],{"disabled":198,"type":199}," The stutters are unrelated to allocation; they're caused by using ",[32,2555,1219],{}," instead of object literals for ",[32,2558,2559],{},"Particle",[191,2561,2563,2565,2566,2568,2569,2571,2572,2574],{"className":2562},[194],[196,2564],{"disabled":198,"type":199}," Switching ",[32,2567,2559],{}," fields from ",[32,2570,2045],{}," to ",[32,2573,371],{}," prevents the stutter by making objects immutable",[191,2576,2578,2580],{"className":2577},[194],[196,2579],{"disabled":198,"type":199}," Constantly allocating and discarding thousands of short-lived objects per second creates heavy garbage collection pressure, and the GC's collection pauses cause the stutters; an object pool that reuses a fixed set of pre-allocated particle objects (resetting their fields instead of reallocating) avoids the allocation churn",[237,2582,2583,2585,2590],{},[240,2584,242],{},[244,2586,2587,2589],{},[247,2588,249],{}," D — Constantly allocating and discarding thousands of short-lived objects per second creates heavy garbage collection pressure, and the GC's collection pauses cause the stutters; an object pool that reuses a fixed set of pre-allocated particle objects (resetting their fields instead of reallocating) avoids the allocation churn",[244,2591,2592,262,2594,2596],{},[247,2593,261],{},[247,2595,1240],{}," High-frequency allocation of short-lived objects fills the young generation heap quickly, triggering frequent minor GC cycles; even brief collection pauses are enough to drop frames in a 60fps loop, producing visible stutter. An object pool sidesteps this by allocating a fixed set of particles once up front and recycling them (resetting position\u002Fvelocity fields) instead of creating and discarding new ones every frame, which drastically cuts allocation churn and GC pressure. Option A blames the wrong scheduler and proposes a strictly worse alternative (see Q12). Options B and C misattribute the cause to unrelated syntax choices that don't affect allocation behavior.",[14,2598,2599,2611,2650],{},[18,2600,2602,2603,2606,2607,2610],{"id":2601},"q17-a-developer-notices-the-app-feels-slow-and-immediately-rewrites-a-for-loop-as-arrayreduce-because-functional-style-is-faster-without-measuring-anything-first-whats-wrong-with-this-approach","Q17. A developer notices the app feels slow and immediately rewrites a ",[32,2604,2605],{},"for"," loop as ",[32,2608,2609],{},"array.reduce()"," because \"functional style is faster,\" without measuring anything first. What's wrong with this approach?",[186,2612,2614,2620,2633,2644],{"className":2613},[189],[191,2615,2617,2619],{"className":2616},[194],[196,2618],{"disabled":198,"type":199}," This is premature optimization based on assumption rather than data; the actual bottleneck could be anywhere (a layout thrash, an unbatched network waterfall, a memory leak), and swapping loop syntax alone rarely matters for performance — the correct approach is to profile first (e.g., the browser's Performance\u002FProfiler panel) to find the actual hot path before changing code",[191,2621,2623,2625,2626,2629,2630,2632],{"className":2622},[194],[196,2624],{"disabled":198,"type":199}," Nothing — ",[32,2627,2628],{},"reduce()"," is always faster than a ",[32,2631,2605],{}," loop in every JS engine",[191,2634,2636,262,2638,2640,2641,2643],{"className":2635},[194],[196,2637],{"disabled":198,"type":199},[32,2639,2628],{}," should always be avoided since it's slower than ",[32,2642,2605],{}," loops in every case",[191,2645,2647,2649],{"className":2646},[194],[196,2648],{"disabled":198,"type":199}," Rewriting loop syntax is the single most impactful optimization available in JavaScript",[237,2651,2652,2654,2659],{},[240,2653,242],{},[244,2655,2656,2658],{},[247,2657,249],{}," A — This is premature optimization based on assumption rather than data; the actual bottleneck could be anywhere (a layout thrash, an unbatched network waterfall, a memory leak), and swapping loop syntax alone rarely matters for performance — the correct approach is to profile first (e.g., the browser's Performance\u002FProfiler panel) to find the actual hot path before changing code",[244,2660,2661,2663,2664,1524,2667,2670,2671,1005,2673,2675],{},[247,2662,261],{}," \"Feels slow\" is a symptom, not a diagnosis — the actual bottleneck is just as likely to be a network waterfall, layout thrashing, an unbounded cache, or a single hot function buried deep in a call stack as it is to be loop syntax, and guessing wastes effort on code that may not matter at all. Profiling tools (flame graphs, the Performance panel, ",[32,2665,2666],{},"console.time",[32,2668,2669],{},"performance.mark"," around suspected hot paths) point directly at where time is actually spent. Options B and D both overstate loop-syntax swaps as a universal win, and option C overstates the opposite — in reality, the difference between ",[32,2672,2605],{},[32,2674,2628],{}," is usually negligible next to real bottlenecks and is engine\u002Fcase-dependent.",[14,2677,2678,2685,2771,2821],{"language":16},[18,2679,2681,2682,2684],{"id":2680},"q18-el-is-a-dom-element-passed-in-from-various-parts-of-the-app-some-are-later-removed-from-the-dom-with-no-other-references-held-whats-the-memory-problem-and-whats-the-direct-fix","Q18. ",[32,2683,316],{}," is a DOM element passed in from various parts of the app; some are later removed from the DOM with no other references held. What's the memory problem, and what's the direct fix?",[23,2686,2687],{"language":16},[26,2688,2690],{"className":28,"code":2689,"language":16,"meta":30,"style":30},"const cache = new Map();\nfunction getMetadata(el) {\n  if (!cache.has(el)) {\n    cache.set(el, computeExpensiveMetadata(el));\n  }\n  return cache.get(el);\n}\n",[32,2691,2692,2706,2719,2736,2752,2756,2767],{"__ignoreMap":30},[35,2693,2694,2696,2698,2700,2702,2704],{"class":37,"line":38},[35,2695,371],{"class":41},[35,2697,496],{"class":59},[35,2699,63],{"class":41},[35,2701,341],{"class":41},[35,2703,503],{"class":45},[35,2705,164],{"class":49},[35,2707,2708,2710,2713,2715,2717],{"class":37,"line":53},[35,2709,42],{"class":41},[35,2711,2712],{"class":45}," getMetadata",[35,2714,72],{"class":49},[35,2716,316],{"class":315},[35,2718,319],{"class":49},[35,2720,2721,2723,2725,2728,2731,2733],{"class":37,"line":82},[35,2722,524],{"class":41},[35,2724,809],{"class":49},[35,2726,2727],{"class":41},"!",[35,2729,2730],{"class":49},"cache.",[35,2732,530],{"class":45},[35,2734,2735],{"class":49},"(el)) {\n",[35,2737,2738,2741,2743,2746,2749],{"class":37,"line":100},[35,2739,2740],{"class":49},"    cache.",[35,2742,567],{"class":45},[35,2744,2745],{"class":49},"(el, ",[35,2747,2748],{"class":45},"computeExpensiveMetadata",[35,2750,2751],{"class":49},"(el));\n",[35,2753,2754],{"class":37,"line":123},[35,2755,2172],{"class":49},[35,2757,2758,2760,2762,2764],{"class":37,"line":134},[35,2759,575],{"class":41},[35,2761,539],{"class":49},[35,2763,542],{"class":45},[35,2765,2766],{"class":49},"(el);\n",[35,2768,2769],{"class":37,"line":146},[35,2770,155],{"class":49},[186,2772,2774,2783,2803,2813],{"className":2773},[189],[191,2775,2777,2779,2780,2782],{"className":2776},[194],[196,2778],{"disabled":198,"type":199}," There's no problem — ",[32,2781,594],{}," automatically drops entries whose keys are no longer referenced elsewhere",[191,2784,2786,262,2788,2791,2792,2794,2795,2797,2798,2403,2800,2802],{"className":2785},[194],[196,2787],{"disabled":198,"type":199},[32,2789,2790],{},"cache"," (a ",[32,2793,594],{},") holds a strong reference to each ",[32,2796,316],{}," key, so as long as an entry exists in the cache, the removed DOM element can never be garbage collected, even though nothing else references it — replacing ",[32,2799,594],{},[32,2801,613],{}," lets those keys (and their entries) be collected once the element has no other referrers",[191,2804,2806,2808,2809,2812],{"className":2805},[194],[196,2807],{"disabled":198,"type":199}," The fix is to call ",[32,2810,2811],{},"cache.clear()"," after every use",[191,2814,2816,262,2818,2820],{"className":2815},[194],[196,2817],{"disabled":198,"type":199},[32,2819,613],{}," would make this worse, since weak references are collected too aggressively and could drop entries while still in use elsewhere",[237,2822,2823,2825,2839],{},[240,2824,242],{},[244,2826,2827,1656,2829,2791,2831,2794,2833,2797,2835,2403,2837,2802],{},[247,2828,249],{},[32,2830,2790],{},[32,2832,594],{},[32,2834,316],{},[32,2836,594],{},[32,2838,613],{},[244,2840,2841,2843,2844,2846,2847,2849,2850,2852,2853,2856,2857,2859,2860,2862],{},[247,2842,261],{}," A regular ",[32,2845,594],{}," retains every key strongly, exactly like any other reference — so simply being a ",[32,2848,594],{}," key is enough to keep an otherwise-unreferenced, detached DOM element alive indefinitely, leaking memory as elements accumulate over the session. ",[32,2851,613],{}," holds its keys ",[679,2854,2855],{},"weakly",": once nothing outside the ",[32,2858,613],{}," references a given key object, the engine is free to collect it, and the corresponding entry disappears automatically. Option A invents automatic eviction ",[32,2861,594],{}," doesn't have. Option C is a workaround that discards the entire cache's usefulness rather than fixing the leak surgically. Option D misunderstands weak references — they're only collected when truly unreachable elsewhere, not \"aggressively.\"",[14,2864,2865,2872,2929,2997],{"language":16},[18,2866,2868,2869,2871],{"id":2867},"q19-having-switched-the-metadata-cache-to-a-weakmap-a-developer-tries-to-run-this-what-happens","Q19. Having switched the metadata cache to a ",[32,2870,613],{},", a developer tries to run this. What happens?",[23,2873,2874],{"language":16},[26,2875,2877],{"className":28,"code":2876,"language":16,"meta":30,"style":30},"console.log(cache.size);\nfor (const [key, value] of cache) {\n  console.log(key, value);\n}\n",[32,2878,2879,2889,2915,2925],{"__ignoreMap":30},[35,2880,2881,2884,2886],{"class":37,"line":38},[35,2882,2883],{"class":49},"console.",[35,2885,140],{"class":45},[35,2887,2888],{"class":49},"(cache.size);\n",[35,2890,2891,2893,2895,2897,2899,2902,2904,2907,2909,2912],{"class":37,"line":53},[35,2892,2605],{"class":41},[35,2894,809],{"class":49},[35,2896,371],{"class":41},[35,2898,1713],{"class":49},[35,2900,2901],{"class":59},"key",[35,2903,790],{"class":49},[35,2905,2906],{"class":59},"value",[35,2908,1816],{"class":49},[35,2910,2911],{"class":41},"of",[35,2913,2914],{"class":49}," cache) {\n",[35,2916,2917,2920,2922],{"class":37,"line":82},[35,2918,2919],{"class":49},"  console.",[35,2921,140],{"class":45},[35,2923,2924],{"class":49},"(key, value);\n",[35,2926,2927],{"class":37,"line":100},[35,2928,155],{"class":49},[186,2930,2932,2948,2963,2985],{"className":2931},[189],[191,2933,2935,262,2937,2940,2941,2944,2945,2947],{"className":2934},[194],[196,2936],{"disabled":198,"type":199},[32,2938,2939],{},"cache.size"," returns the count correctly, but ",[32,2942,2943],{},"for...of"," throws because ",[32,2946,613],{}," is not iterable",[191,2949,2951,2953,2954,2956,2957,2959,2960,2962],{"className":2950},[194],[196,2952],{"disabled":198,"type":199}," Both work identically to ",[32,2955,594],{},", since ",[32,2958,613],{}," is just a ",[32,2961,594],{}," with automatic cleanup",[191,2964,2966,262,2968,2970,2971,2974,2975,1524,2978,2981,2982,2984],{"className":2965},[194],[196,2967],{"disabled":198,"type":199},[32,2969,613],{}," has neither ",[32,2972,2973],{},".size"," nor ",[32,2976,2977],{},".keys()",[32,2979,2980],{},".entries()","\u002Fiteration support at all — this is deliberate, since the collection's contents can change at any moment as the engine garbage-collects keys, so exposing enumeration would make behavior non-deterministic; if you need to enumerate or count entries, a regular ",[32,2983,594],{}," (with manual cleanup) is the right tool instead",[191,2986,2988,262,2990,2992,2993,2996],{"className":2987},[194],[196,2989],{"disabled":198,"type":199},[32,2991,2939],{}," throws a ",[32,2994,2995],{},"TypeError",", but iteration is supported and returns only the currently-live entries",[237,2998,2999,3001,3015],{},[240,3000,242],{},[244,3002,3003,1229,3005,2970,3007,2974,3009,1524,3011,2981,3013,2984],{},[247,3004,249],{},[32,3006,613],{},[32,3008,2973],{},[32,3010,2977],{},[32,3012,2980],{},[32,3014,594],{},[244,3016,3017,262,3019,262,3021,3024,3025,3027,3028,3030,3031,3034,3035,3037,3038,3040,3041,3043,3044,2974,3047,3049,3050,3052,3053,2403,3055,3057,3058,3060],{},[247,3018,261],{},[247,3020,265],{},[32,3022,3023],{},"console.log(cache.size)"," logs ",[32,3026,1636],{}," (no ",[32,3029,2973],{}," getter exists on ",[32,3032,3033],{},"WeakMap.prototype","), and the ",[32,3036,2943],{}," loop throws a ",[32,3039,2995],{}," because ",[32,3042,613],{}," implements neither ",[32,3045,3046],{},"Symbol.iterator",[32,3048,2980],{},". This is an intentional spec design: garbage collection timing is non-deterministic and implementation-defined, so if you could enumerate a ",[32,3051,613],{},"'s contents, the results (and even whether a collection pass happened mid-iteration) would be observable and inconsistent across engines — the spec avoids that entirely by disallowing enumeration. Options A and D each get one half right but the other half wrong. Option B conflates ",[32,3054,613],{},[32,3056,594],{},", which does support both ",[32,3059,2973],{}," and iteration.",[14,3062,3063,3071,3118],{},[18,3064,3066,3067,3070],{"id":3065},"q20-a-profiler-flame-graph-shows-80-of-a-slow-interactions-time-inside-a-single-recalculatelayout-function-called-synchronously-once-per-scroll-event-dozens-of-times-per-second-whats-the-most-appropriate-first-fix","Q20. A profiler flame graph shows 80% of a slow interaction's time inside a single ",[32,3068,3069],{},"recalculateLayout()"," function, called synchronously once per scroll event (dozens of times per second). What's the most appropriate first fix?",[186,3072,3074,3080,3091,3106],{"className":3073},[189],[191,3075,3077,3079],{"className":3076},[194],[196,3078],{"disabled":198,"type":199}," Rewrite the entire app in a different framework, since the framework is likely the root cause",[191,3081,3083,2413,3085,1524,3087,3090],{"className":3082},[194],[196,3084],{"disabled":198,"type":199},[32,3086,2666],{},[32,3088,3089],{},"console.timeEnd"," calls throughout the codebase before making any change, since more logging is always the correct first step",[191,3092,3094,3096,3097,3100,3101,1524,3103,3105],{"className":3093},[194],[196,3095],{"disabled":198,"type":199}," Convert all ",[32,3098,3099],{},"var"," declarations in the file to ",[32,3102,371],{},[32,3104,2045],{},", since modern syntax is inherently faster",[191,3107,3109,3111,3112,3114,3115,3117],{"className":3108},[194],[196,3110],{"disabled":198,"type":199}," Throttle the scroll handler so ",[32,3113,3069],{}," runs at a capped rate (or move the work to ",[32,3116,1961],{},") — this directly targets the measured bottleneck (call frequency) without speculative, unrelated changes",[237,3119,3120,3122,3131],{},[240,3121,242],{},[244,3123,3124,3126,3127,3114,3129,3117],{},[247,3125,249],{}," D — Throttle the scroll handler so ",[32,3128,3069],{},[32,3130,1961],{},[244,3132,3133,3135,3136,3138],{},[247,3134,261],{}," The profiler already identified the exact bottleneck: an expensive function invoked far more often than necessary on a high-frequency event. The direct, evidence-backed fix is to cut the call frequency — throttling, or batching the work into a single ",[32,3137,1961],{}," callback per visual frame — rather than guessing at unrelated changes. Option B misunderstands the situation: the profiling data already exists, so adding scattered manual timers is redundant busywork, not \"the correct first step.\" Options A and C are classic cargo-cult \"optimizations\" — a framework rewrite and syntax modernization — that don't address the measured cause at all and risk introducing new bugs for no measured benefit.",[3140,3141,3142],"style",{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}",{"title":30,"searchDepth":53,"depth":53,"links":3144},[3145,3146,3148,3149,3150,3151,3152,3154,3155,3157,3159,3161,3163,3164,3165,3167,3168,3170,3172,3174],{"id":20,"depth":82,"text":21},{"id":291,"depth":82,"text":3147},"Q2. What's the memory\u002Fbehavior consequence of removing clockEl without clearing the interval?",{"id":481,"depth":82,"text":482},{"id":649,"depth":82,"text":650},{"id":707,"depth":82,"text":708},{"id":767,"depth":82,"text":768},{"id":1060,"depth":82,"text":3153},"Q7. What perf benefit does V8 get from a and b being built with the same property order and types?",{"id":1264,"depth":82,"text":1265},{"id":1417,"depth":82,"text":3156},"Q9. getArea(shape) reads shape.width * shape.height and runs thousands of times per frame. Why is it faster when every shape has the same property layout than when shapes vary?",{"id":1533,"depth":82,"text":3158},"Q10. In a hot path, what's the perf concern with delete cfg.debug, and what's the preferred alternative?",{"id":1681,"depth":82,"text":3160},"Q11. Why can iterating\u002Fsumming dense be significantly faster than doing the same over sparse or alsoSparse?",{"id":1914,"depth":82,"text":3162},"Q12. An animation loop uses setInterval(update, 16) instead of requestAnimationFrame(update). Beyond potential drift from the display's refresh rate, what's another concrete downside in production?",{"id":2001,"depth":82,"text":2002},{"id":2319,"depth":82,"text":2320},{"id":2385,"depth":82,"text":3166},"Q15. A page renders a scrollable table of 50,000 rows by mapping the full dataset directly to 50,000 \u003Ctr> elements, causing visible jank. What's the standard fix, and why does it help?",{"id":2460,"depth":82,"text":2461},{"id":2601,"depth":82,"text":3169},"Q17. A developer notices the app feels slow and immediately rewrites a for loop as array.reduce() because \"functional style is faster,\" without measuring anything first. What's wrong with this approach?",{"id":2680,"depth":82,"text":3171},"Q18. el is a DOM element passed in from various parts of the app; some are later removed from the DOM with no other references held. What's the memory problem, and what's the direct fix?",{"id":2867,"depth":82,"text":3173},"Q19. Having switched the metadata cache to a WeakMap, a developer tries to run this. What happens?",{"id":3065,"depth":82,"text":3175},"Q20. A profiler flame graph shows 80% of a slow interaction's time inside a single recalculateLayout() function, called synchronously once per scroll event (dozens of times per second). What's the most appropriate first fix?","md",{},"\u002Fjs\u002F26-performance-and-optimization",{"title":5,"description":30},"js\u002F26-performance-and-optimization","iiEdztdP3x6sfaV7LQodrgUsKiM7-b6FISZPQkPo9KA",1787335397337]