[{"data":1,"prerenderedAt":3225},["ShallowReactive",2],{"page-\u002Fjs\u002F12-closures-and-lexical-scope":3},{"id":4,"title":5,"body":6,"description":3218,"extension":3219,"meta":3220,"navigation":58,"path":3221,"seo":3222,"stem":3223,"__hash__":3224},"content\u002Fjs\u002F12-closures-and-lexical-scope.md","JavaScript 12 — Closure Internals: Lexical Environment, Capture & Memory Leaks",{"type":7,"value":8,"toc":3208},"minimark",[9,14,19,389,393,689,693,1233,1237,1611,1615,2091,2095,2563,2567,2968,2972,2976,3107,3204],[10,11,13],"h1",{"id":12},"_12-closure-internals-lexical-environment-capture-memory-leaks","12 — Closure Internals: Lexical Environment, Capture & Memory Leaks",[15,16,18],"h2",{"id":17},"lexical-environment-and-the-scope-chain","Lexical Environment and the Scope Chain",[20,21,23],"code-wrapper",{"language":22},"javascript",[24,25,29],"pre",{"className":26,"code":27,"language":22,"meta":28,"style":28},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F ── Every function captures its lexical environment (the scope where it was defined) ──\n\u002F\u002F A closure is a function + its captured lexical environment.\n\u002F\u002F Even after the outer function returns, the inner function retains access to the outer's variables.\n\nfunction outer() {\n    const message = \"hello from outer\";  \u002F\u002F captured by the inner closure\n\n    function inner() {\n        \u002F\u002F `inner` has a reference to outer's lexical environment\n        console.log(message);  \u002F\u002F still accessible after outer returns\n    }\n\n    return inner;  \u002F\u002F return the closure\n}\n\nconst closure = outer();  \u002F\u002F outer returns, but `message` is captured\nclosure();  \u002F\u002F \"hello from outer\" — `message` is alive (not garbage collected)\n\n\u002F\u002F ── The scope chain: nested lexical environments ──\nfunction a() {\n    const x = 1;\n    function b() {\n        const y = 2;\n        function c() {\n            const z = 3;\n            \u002F\u002F c's scope chain: c → b → a → global\n            \u002F\u002F c can access z (own), y (b's), x (a's), and globals\n            console.log(x, y, z);  \u002F\u002F 1 2 3\n        }\n        c();\n    }\n    b();\n}\n\n\u002F\u002F ── How the engine stores closures ──\n\u002F\u002F Each execution context has a Lexical Environment (a record of variable bindings + a link to the outer environment).\n\u002F\u002F When a function is created, it captures a reference to the current lexical environment.\n\u002F\u002F The function's [[Environment]] internal slot points to this environment.\n\u002F\u002F When the function is called, a new environment is created with the outer link = the captured environment.\n\u002F\u002F Variable lookup follows the chain: own env → outer env → ... → global env.\n","",[30,31,32,41,47,53,60,75,98,103,114,120,135,141,146,158,164,169,188,199,204,210,220,236,246,262,273,289,295,301,315,321,330,335,343,348,353,359,365,371,377,383],"code",{"__ignoreMap":28},[33,34,37],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sdCPZ","\u002F\u002F ── Every function captures its lexical environment (the scope where it was defined) ──\n",[33,42,44],{"class":35,"line":43},2,[33,45,46],{"class":39},"\u002F\u002F A closure is a function + its captured lexical environment.\n",[33,48,50],{"class":35,"line":49},3,[33,51,52],{"class":39},"\u002F\u002F Even after the outer function returns, the inner function retains access to the outer's variables.\n",[33,54,56],{"class":35,"line":55},4,[33,57,59],{"emptyLinePlaceholder":58},true,"\n",[33,61,63,67,71],{"class":35,"line":62},5,[33,64,66],{"class":65},"svdQ7","function",[33,68,70],{"class":69},"sIsaT"," outer",[33,72,74],{"class":73},"ssxIu","() {\n",[33,76,78,81,85,88,92,95],{"class":35,"line":77},6,[33,79,80],{"class":65},"    const",[33,82,84],{"class":83},"snvgF"," message",[33,86,87],{"class":65}," =",[33,89,91],{"class":90},"sJ6F3"," \"hello from outer\"",[33,93,94],{"class":73},";  ",[33,96,97],{"class":39},"\u002F\u002F captured by the inner closure\n",[33,99,101],{"class":35,"line":100},7,[33,102,59],{"emptyLinePlaceholder":58},[33,104,106,109,112],{"class":35,"line":105},8,[33,107,108],{"class":65},"    function",[33,110,111],{"class":69}," inner",[33,113,74],{"class":73},[33,115,117],{"class":35,"line":116},9,[33,118,119],{"class":39},"        \u002F\u002F `inner` has a reference to outer's lexical environment\n",[33,121,123,126,129,132],{"class":35,"line":122},10,[33,124,125],{"class":73},"        console.",[33,127,128],{"class":69},"log",[33,130,131],{"class":73},"(message);  ",[33,133,134],{"class":39},"\u002F\u002F still accessible after outer returns\n",[33,136,138],{"class":35,"line":137},11,[33,139,140],{"class":73},"    }\n",[33,142,144],{"class":35,"line":143},12,[33,145,59],{"emptyLinePlaceholder":58},[33,147,149,152,155],{"class":35,"line":148},13,[33,150,151],{"class":65},"    return",[33,153,154],{"class":73}," inner;  ",[33,156,157],{"class":39},"\u002F\u002F return the closure\n",[33,159,161],{"class":35,"line":160},14,[33,162,163],{"class":73},"}\n",[33,165,167],{"class":35,"line":166},15,[33,168,59],{"emptyLinePlaceholder":58},[33,170,172,175,178,180,182,185],{"class":35,"line":171},16,[33,173,174],{"class":65},"const",[33,176,177],{"class":83}," closure",[33,179,87],{"class":65},[33,181,70],{"class":69},[33,183,184],{"class":73},"();  ",[33,186,187],{"class":39},"\u002F\u002F outer returns, but `message` is captured\n",[33,189,191,194,196],{"class":35,"line":190},17,[33,192,193],{"class":69},"closure",[33,195,184],{"class":73},[33,197,198],{"class":39},"\u002F\u002F \"hello from outer\" — `message` is alive (not garbage collected)\n",[33,200,202],{"class":35,"line":201},18,[33,203,59],{"emptyLinePlaceholder":58},[33,205,207],{"class":35,"line":206},19,[33,208,209],{"class":39},"\u002F\u002F ── The scope chain: nested lexical environments ──\n",[33,211,213,215,218],{"class":35,"line":212},20,[33,214,66],{"class":65},[33,216,217],{"class":69}," a",[33,219,74],{"class":73},[33,221,223,225,228,230,233],{"class":35,"line":222},21,[33,224,80],{"class":65},[33,226,227],{"class":83}," x",[33,229,87],{"class":65},[33,231,232],{"class":83}," 1",[33,234,235],{"class":73},";\n",[33,237,239,241,244],{"class":35,"line":238},22,[33,240,108],{"class":65},[33,242,243],{"class":69}," b",[33,245,74],{"class":73},[33,247,249,252,255,257,260],{"class":35,"line":248},23,[33,250,251],{"class":65},"        const",[33,253,254],{"class":83}," y",[33,256,87],{"class":65},[33,258,259],{"class":83}," 2",[33,261,235],{"class":73},[33,263,265,268,271],{"class":35,"line":264},24,[33,266,267],{"class":65},"        function",[33,269,270],{"class":69}," c",[33,272,74],{"class":73},[33,274,276,279,282,284,287],{"class":35,"line":275},25,[33,277,278],{"class":65},"            const",[33,280,281],{"class":83}," z",[33,283,87],{"class":65},[33,285,286],{"class":83}," 3",[33,288,235],{"class":73},[33,290,292],{"class":35,"line":291},26,[33,293,294],{"class":39},"            \u002F\u002F c's scope chain: c → b → a → global\n",[33,296,298],{"class":35,"line":297},27,[33,299,300],{"class":39},"            \u002F\u002F c can access z (own), y (b's), x (a's), and globals\n",[33,302,304,307,309,312],{"class":35,"line":303},28,[33,305,306],{"class":73},"            console.",[33,308,128],{"class":69},[33,310,311],{"class":73},"(x, y, z);  ",[33,313,314],{"class":39},"\u002F\u002F 1 2 3\n",[33,316,318],{"class":35,"line":317},29,[33,319,320],{"class":73},"        }\n",[33,322,324,327],{"class":35,"line":323},30,[33,325,326],{"class":69},"        c",[33,328,329],{"class":73},"();\n",[33,331,333],{"class":35,"line":332},31,[33,334,140],{"class":73},[33,336,338,341],{"class":35,"line":337},32,[33,339,340],{"class":69},"    b",[33,342,329],{"class":73},[33,344,346],{"class":35,"line":345},33,[33,347,163],{"class":73},[33,349,351],{"class":35,"line":350},34,[33,352,59],{"emptyLinePlaceholder":58},[33,354,356],{"class":35,"line":355},35,[33,357,358],{"class":39},"\u002F\u002F ── How the engine stores closures ──\n",[33,360,362],{"class":35,"line":361},36,[33,363,364],{"class":39},"\u002F\u002F Each execution context has a Lexical Environment (a record of variable bindings + a link to the outer environment).\n",[33,366,368],{"class":35,"line":367},37,[33,369,370],{"class":39},"\u002F\u002F When a function is created, it captures a reference to the current lexical environment.\n",[33,372,374],{"class":35,"line":373},38,[33,375,376],{"class":39},"\u002F\u002F The function's [[Environment]] internal slot points to this environment.\n",[33,378,380],{"class":35,"line":379},39,[33,381,382],{"class":39},"\u002F\u002F When the function is called, a new environment is created with the outer link = the captured environment.\n",[33,384,386],{"class":35,"line":385},40,[33,387,388],{"class":39},"\u002F\u002F Variable lookup follows the chain: own env → outer env → ... → global env.\n",[15,390,392],{"id":391},"the-classic-loop-closure-bug","The Classic Loop Closure Bug",[20,394,395],{"language":22},[24,396,398],{"className":26,"code":397,"language":22,"meta":28,"style":28},"\u002F\u002F ── The #1 closure gotcha: var in a loop captures the SAME variable ──\n\u002F\u002F ❌ NAIVE — all closures share the same `i` (var is function-scoped, not block-scoped)\nfor (var i = 0; i \u003C 3; i++) {\n    setTimeout(() => console.log(i), 0);\n}\n\u002F\u002F Output: 3, 3, 3 — by the time the callbacks run, `i` is 3 (all share the same binding)\n\n\u002F\u002F ✅ CORRECT — let creates a new binding per iteration (block scope)\nfor (let i = 0; i \u003C 3; i++) {\n    setTimeout(() => console.log(i), 0);\n}\n\u002F\u002F Output: 0, 1, 2 — each iteration's `i` is a separate binding (per-iteration scope)\n\n\u002F\u002F ✅ IIFE workaround (pre-ES6, before let)\nfor (var i = 0; i \u003C 3; i++) {\n    (function(j) {  \u002F\u002F IIFE creates a new scope, captures the current value of i\n        setTimeout(() => console.log(j), 0);\n    })(i);  \u002F\u002F pass i as an argument → j is a copy\n}\n\u002F\u002F Output: 0, 1, 2\n\n\u002F\u002F ── How `let` in loops works internally ──\n\u002F\u002F `for (let i = 0; i \u003C 3; i++)` is desugared to create a NEW binding per iteration:\n\u002F\u002F   { let i = 0; setTimeout(() => console.log(i), 0); }\n\u002F\u002F   { let i = 1; setTimeout(() => console.log(i), 0); }\n\u002F\u002F   { let i = 2; setTimeout(() => console.log(i), 0); }\n\u002F\u002F Each iteration block has its own `i` — closures capture different bindings.\n\n\u002F\u002F ── With `var`, it's one binding for the entire loop ──\n\u002F\u002F   var i; i = 0; setTimeout(() => console.log(i), 0); i++; ...\n\u002F\u002F All three setTimeout callbacks capture the SAME `i` — which is 3 by the time they run.\n",[30,399,400,405,410,447,472,476,481,485,490,517,535,539,544,548,553,579,599,619,627,631,636,640,645,650,655,660,665,670,674,679,684],{"__ignoreMap":28},[33,401,402],{"class":35,"line":36},[33,403,404],{"class":39},"\u002F\u002F ── The #1 closure gotcha: var in a loop captures the SAME variable ──\n",[33,406,407],{"class":35,"line":43},[33,408,409],{"class":39},"\u002F\u002F ❌ NAIVE — all closures share the same `i` (var is function-scoped, not block-scoped)\n",[33,411,412,415,418,421,424,427,430,433,436,438,441,444],{"class":35,"line":49},[33,413,414],{"class":65},"for",[33,416,417],{"class":73}," (",[33,419,420],{"class":65},"var",[33,422,423],{"class":73}," i ",[33,425,426],{"class":65},"=",[33,428,429],{"class":83}," 0",[33,431,432],{"class":73},"; i ",[33,434,435],{"class":65},"\u003C",[33,437,286],{"class":83},[33,439,440],{"class":73},"; i",[33,442,443],{"class":65},"++",[33,445,446],{"class":73},") {\n",[33,448,449,452,455,458,461,463,466,469],{"class":35,"line":55},[33,450,451],{"class":69},"    setTimeout",[33,453,454],{"class":73},"(() ",[33,456,457],{"class":65},"=>",[33,459,460],{"class":73}," console.",[33,462,128],{"class":69},[33,464,465],{"class":73},"(i), ",[33,467,468],{"class":83},"0",[33,470,471],{"class":73},");\n",[33,473,474],{"class":35,"line":62},[33,475,163],{"class":73},[33,477,478],{"class":35,"line":77},[33,479,480],{"class":39},"\u002F\u002F Output: 3, 3, 3 — by the time the callbacks run, `i` is 3 (all share the same binding)\n",[33,482,483],{"class":35,"line":100},[33,484,59],{"emptyLinePlaceholder":58},[33,486,487],{"class":35,"line":105},[33,488,489],{"class":39},"\u002F\u002F ✅ CORRECT — let creates a new binding per iteration (block scope)\n",[33,491,492,494,496,499,501,503,505,507,509,511,513,515],{"class":35,"line":116},[33,493,414],{"class":65},[33,495,417],{"class":73},[33,497,498],{"class":65},"let",[33,500,423],{"class":73},[33,502,426],{"class":65},[33,504,429],{"class":83},[33,506,432],{"class":73},[33,508,435],{"class":65},[33,510,286],{"class":83},[33,512,440],{"class":73},[33,514,443],{"class":65},[33,516,446],{"class":73},[33,518,519,521,523,525,527,529,531,533],{"class":35,"line":122},[33,520,451],{"class":69},[33,522,454],{"class":73},[33,524,457],{"class":65},[33,526,460],{"class":73},[33,528,128],{"class":69},[33,530,465],{"class":73},[33,532,468],{"class":83},[33,534,471],{"class":73},[33,536,537],{"class":35,"line":137},[33,538,163],{"class":73},[33,540,541],{"class":35,"line":143},[33,542,543],{"class":39},"\u002F\u002F Output: 0, 1, 2 — each iteration's `i` is a separate binding (per-iteration scope)\n",[33,545,546],{"class":35,"line":148},[33,547,59],{"emptyLinePlaceholder":58},[33,549,550],{"class":35,"line":160},[33,551,552],{"class":39},"\u002F\u002F ✅ IIFE workaround (pre-ES6, before let)\n",[33,554,555,557,559,561,563,565,567,569,571,573,575,577],{"class":35,"line":166},[33,556,414],{"class":65},[33,558,417],{"class":73},[33,560,420],{"class":65},[33,562,423],{"class":73},[33,564,426],{"class":65},[33,566,429],{"class":83},[33,568,432],{"class":73},[33,570,435],{"class":65},[33,572,286],{"class":83},[33,574,440],{"class":73},[33,576,443],{"class":65},[33,578,446],{"class":73},[33,580,581,584,586,589,593,596],{"class":35,"line":171},[33,582,583],{"class":73},"    (",[33,585,66],{"class":65},[33,587,588],{"class":73},"(",[33,590,592],{"class":591},"sCrzJ","j",[33,594,595],{"class":73},") {  ",[33,597,598],{"class":39},"\u002F\u002F IIFE creates a new scope, captures the current value of i\n",[33,600,601,604,606,608,610,612,615,617],{"class":35,"line":190},[33,602,603],{"class":69},"        setTimeout",[33,605,454],{"class":73},[33,607,457],{"class":65},[33,609,460],{"class":73},[33,611,128],{"class":69},[33,613,614],{"class":73},"(j), ",[33,616,468],{"class":83},[33,618,471],{"class":73},[33,620,621,624],{"class":35,"line":201},[33,622,623],{"class":73},"    })(i);  ",[33,625,626],{"class":39},"\u002F\u002F pass i as an argument → j is a copy\n",[33,628,629],{"class":35,"line":206},[33,630,163],{"class":73},[33,632,633],{"class":35,"line":212},[33,634,635],{"class":39},"\u002F\u002F Output: 0, 1, 2\n",[33,637,638],{"class":35,"line":222},[33,639,59],{"emptyLinePlaceholder":58},[33,641,642],{"class":35,"line":238},[33,643,644],{"class":39},"\u002F\u002F ── How `let` in loops works internally ──\n",[33,646,647],{"class":35,"line":248},[33,648,649],{"class":39},"\u002F\u002F `for (let i = 0; i \u003C 3; i++)` is desugared to create a NEW binding per iteration:\n",[33,651,652],{"class":35,"line":264},[33,653,654],{"class":39},"\u002F\u002F   { let i = 0; setTimeout(() => console.log(i), 0); }\n",[33,656,657],{"class":35,"line":275},[33,658,659],{"class":39},"\u002F\u002F   { let i = 1; setTimeout(() => console.log(i), 0); }\n",[33,661,662],{"class":35,"line":291},[33,663,664],{"class":39},"\u002F\u002F   { let i = 2; setTimeout(() => console.log(i), 0); }\n",[33,666,667],{"class":35,"line":297},[33,668,669],{"class":39},"\u002F\u002F Each iteration block has its own `i` — closures capture different bindings.\n",[33,671,672],{"class":35,"line":303},[33,673,59],{"emptyLinePlaceholder":58},[33,675,676],{"class":35,"line":317},[33,677,678],{"class":39},"\u002F\u002F ── With `var`, it's one binding for the entire loop ──\n",[33,680,681],{"class":35,"line":323},[33,682,683],{"class":39},"\u002F\u002F   var i; i = 0; setTimeout(() => console.log(i), 0); i++; ...\n",[33,685,686],{"class":35,"line":332},[33,687,688],{"class":39},"\u002F\u002F All three setTimeout callbacks capture the SAME `i` — which is 3 by the time they run.\n",[15,690,692],{"id":691},"closure-for-encapsulation-module-pattern","Closure for Encapsulation (Module Pattern)",[20,694,695],{"language":22},[24,696,698],{"className":26,"code":697,"language":22,"meta":28,"style":28},"\u002F\u002F ── Closures provide private state (encapsulation without classes) ──\nfunction createCounter(initial = 0) {\n    let count = initial;  \u002F\u002F private (closure-captured, not accessible outside)\n\n    return {\n        increment() { return ++count; },  \u002F\u002F closure over `count`\n        decrement() { return --count; },\n        getCount() { return count; },     \u002F\u002F read-only access via method\n        reset() { count = initial; },\n    };\n}\n\nconst counter = createCounter(10);\ncounter.increment();  \u002F\u002F 11\ncounter.increment();  \u002F\u002F 12\nconsole.log(counter.getCount());  \u002F\u002F 12\n\u002F\u002F counter.count;  \u002F\u002F undefined — `count` is private (not a property, it's a closure variable)\n\u002F\u002F The only way to access `count` is through the methods (encapsulation).\n\n\u002F\u002F ── Multiple independent instances (each closure has its own `count`) ──\nconst c1 = createCounter(0);\nconst c2 = createCounter(100);\nc1.increment();  \u002F\u002F 1\nc2.decrement();  \u002F\u002F 99\nconsole.log(c1.getCount(), c2.getCount());  \u002F\u002F 1 99 (independent private state)\n\n\u002F\u002F ── Closure-based memoization (cache function results) ──\nfunction memoize(fn) {\n    const cache = new Map();  \u002F\u002F private cache (closure-captured)\n    return function(...args) {\n        const key = JSON.stringify(args);\n        if (cache.has(key)) return cache.get(key);  \u002F\u002F cache hit\n        const result = fn.apply(this, args);\n        cache.set(key, result);  \u002F\u002F cache miss → store\n        return result;\n    };\n}\nconst slowFib = (n) => n \u003C= 1 ? n : slowFib(n - 1) + slowFib(n - 2);\nconst fastFib = memoize(slowFib);\n\u002F\u002F fastFib(40);  \u002F\u002F fast (cached) — O(n) instead of O(2^n)\n",[30,699,700,705,723,739,743,750,770,785,800,813,818,822,826,844,857,868,886,894,899,903,908,925,943,955,968,989,993,998,1012,1032,1049,1070,1098,1121,1135,1143,1147,1151,1211,1225],{"__ignoreMap":28},[33,701,702],{"class":35,"line":36},[33,703,704],{"class":39},"\u002F\u002F ── Closures provide private state (encapsulation without classes) ──\n",[33,706,707,709,712,714,717,719,721],{"class":35,"line":43},[33,708,66],{"class":65},[33,710,711],{"class":69}," createCounter",[33,713,588],{"class":73},[33,715,716],{"class":591},"initial",[33,718,87],{"class":65},[33,720,429],{"class":83},[33,722,446],{"class":73},[33,724,725,728,731,733,736],{"class":35,"line":49},[33,726,727],{"class":65},"    let",[33,729,730],{"class":73}," count ",[33,732,426],{"class":65},[33,734,735],{"class":73}," initial;  ",[33,737,738],{"class":39},"\u002F\u002F private (closure-captured, not accessible outside)\n",[33,740,741],{"class":35,"line":55},[33,742,59],{"emptyLinePlaceholder":58},[33,744,745,747],{"class":35,"line":62},[33,746,151],{"class":65},[33,748,749],{"class":73}," {\n",[33,751,752,755,758,761,764,767],{"class":35,"line":77},[33,753,754],{"class":69},"        increment",[33,756,757],{"class":73},"() { ",[33,759,760],{"class":65},"return",[33,762,763],{"class":65}," ++",[33,765,766],{"class":73},"count; },  ",[33,768,769],{"class":39},"\u002F\u002F closure over `count`\n",[33,771,772,775,777,779,782],{"class":35,"line":100},[33,773,774],{"class":69},"        decrement",[33,776,757],{"class":73},[33,778,760],{"class":65},[33,780,781],{"class":65}," --",[33,783,784],{"class":73},"count; },\n",[33,786,787,790,792,794,797],{"class":35,"line":105},[33,788,789],{"class":69},"        getCount",[33,791,757],{"class":73},[33,793,760],{"class":65},[33,795,796],{"class":73}," count; },     ",[33,798,799],{"class":39},"\u002F\u002F read-only access via method\n",[33,801,802,805,808,810],{"class":35,"line":116},[33,803,804],{"class":69},"        reset",[33,806,807],{"class":73},"() { count ",[33,809,426],{"class":65},[33,811,812],{"class":73}," initial; },\n",[33,814,815],{"class":35,"line":122},[33,816,817],{"class":73},"    };\n",[33,819,820],{"class":35,"line":137},[33,821,163],{"class":73},[33,823,824],{"class":35,"line":143},[33,825,59],{"emptyLinePlaceholder":58},[33,827,828,830,833,835,837,839,842],{"class":35,"line":148},[33,829,174],{"class":65},[33,831,832],{"class":83}," counter",[33,834,87],{"class":65},[33,836,711],{"class":69},[33,838,588],{"class":73},[33,840,841],{"class":83},"10",[33,843,471],{"class":73},[33,845,846,849,852,854],{"class":35,"line":160},[33,847,848],{"class":73},"counter.",[33,850,851],{"class":69},"increment",[33,853,184],{"class":73},[33,855,856],{"class":39},"\u002F\u002F 11\n",[33,858,859,861,863,865],{"class":35,"line":166},[33,860,848],{"class":73},[33,862,851],{"class":69},[33,864,184],{"class":73},[33,866,867],{"class":39},"\u002F\u002F 12\n",[33,869,870,873,875,878,881,884],{"class":35,"line":171},[33,871,872],{"class":73},"console.",[33,874,128],{"class":69},[33,876,877],{"class":73},"(counter.",[33,879,880],{"class":69},"getCount",[33,882,883],{"class":73},"());  ",[33,885,867],{"class":39},[33,887,888,891],{"class":35,"line":190},[33,889,890],{"class":39},"\u002F\u002F counter.count;",[33,892,893],{"class":39},"  \u002F\u002F undefined — `count` is private (not a property, it's a closure variable)\n",[33,895,896],{"class":35,"line":201},[33,897,898],{"class":39},"\u002F\u002F The only way to access `count` is through the methods (encapsulation).\n",[33,900,901],{"class":35,"line":206},[33,902,59],{"emptyLinePlaceholder":58},[33,904,905],{"class":35,"line":212},[33,906,907],{"class":39},"\u002F\u002F ── Multiple independent instances (each closure has its own `count`) ──\n",[33,909,910,912,915,917,919,921,923],{"class":35,"line":222},[33,911,174],{"class":65},[33,913,914],{"class":83}," c1",[33,916,87],{"class":65},[33,918,711],{"class":69},[33,920,588],{"class":73},[33,922,468],{"class":83},[33,924,471],{"class":73},[33,926,927,929,932,934,936,938,941],{"class":35,"line":238},[33,928,174],{"class":65},[33,930,931],{"class":83}," c2",[33,933,87],{"class":65},[33,935,711],{"class":69},[33,937,588],{"class":73},[33,939,940],{"class":83},"100",[33,942,471],{"class":73},[33,944,945,948,950,952],{"class":35,"line":248},[33,946,947],{"class":73},"c1.",[33,949,851],{"class":69},[33,951,184],{"class":73},[33,953,954],{"class":39},"\u002F\u002F 1\n",[33,956,957,960,963,965],{"class":35,"line":264},[33,958,959],{"class":73},"c2.",[33,961,962],{"class":69},"decrement",[33,964,184],{"class":73},[33,966,967],{"class":39},"\u002F\u002F 99\n",[33,969,970,972,974,977,979,982,984,986],{"class":35,"line":275},[33,971,872],{"class":73},[33,973,128],{"class":69},[33,975,976],{"class":73},"(c1.",[33,978,880],{"class":69},[33,980,981],{"class":73},"(), c2.",[33,983,880],{"class":69},[33,985,883],{"class":73},[33,987,988],{"class":39},"\u002F\u002F 1 99 (independent private state)\n",[33,990,991],{"class":35,"line":291},[33,992,59],{"emptyLinePlaceholder":58},[33,994,995],{"class":35,"line":297},[33,996,997],{"class":39},"\u002F\u002F ── Closure-based memoization (cache function results) ──\n",[33,999,1000,1002,1005,1007,1010],{"class":35,"line":303},[33,1001,66],{"class":65},[33,1003,1004],{"class":69}," memoize",[33,1006,588],{"class":73},[33,1008,1009],{"class":591},"fn",[33,1011,446],{"class":73},[33,1013,1014,1016,1019,1021,1024,1027,1029],{"class":35,"line":317},[33,1015,80],{"class":65},[33,1017,1018],{"class":83}," cache",[33,1020,87],{"class":65},[33,1022,1023],{"class":65}," new",[33,1025,1026],{"class":69}," Map",[33,1028,184],{"class":73},[33,1030,1031],{"class":39},"\u002F\u002F private cache (closure-captured)\n",[33,1033,1034,1036,1039,1041,1044,1047],{"class":35,"line":323},[33,1035,151],{"class":65},[33,1037,1038],{"class":65}," function",[33,1040,588],{"class":73},[33,1042,1043],{"class":65},"...",[33,1045,1046],{"class":591},"args",[33,1048,446],{"class":73},[33,1050,1051,1053,1056,1058,1061,1064,1067],{"class":35,"line":332},[33,1052,251],{"class":65},[33,1054,1055],{"class":83}," key",[33,1057,87],{"class":65},[33,1059,1060],{"class":83}," JSON",[33,1062,1063],{"class":73},".",[33,1065,1066],{"class":69},"stringify",[33,1068,1069],{"class":73},"(args);\n",[33,1071,1072,1075,1078,1081,1084,1086,1089,1092,1095],{"class":35,"line":337},[33,1073,1074],{"class":65},"        if",[33,1076,1077],{"class":73}," (cache.",[33,1079,1080],{"class":69},"has",[33,1082,1083],{"class":73},"(key)) ",[33,1085,760],{"class":65},[33,1087,1088],{"class":73}," cache.",[33,1090,1091],{"class":69},"get",[33,1093,1094],{"class":73},"(key);  ",[33,1096,1097],{"class":39},"\u002F\u002F cache hit\n",[33,1099,1100,1102,1105,1107,1110,1113,1115,1118],{"class":35,"line":345},[33,1101,251],{"class":65},[33,1103,1104],{"class":83}," result",[33,1106,87],{"class":65},[33,1108,1109],{"class":73}," fn.",[33,1111,1112],{"class":69},"apply",[33,1114,588],{"class":73},[33,1116,1117],{"class":83},"this",[33,1119,1120],{"class":73},", args);\n",[33,1122,1123,1126,1129,1132],{"class":35,"line":350},[33,1124,1125],{"class":73},"        cache.",[33,1127,1128],{"class":69},"set",[33,1130,1131],{"class":73},"(key, result);  ",[33,1133,1134],{"class":39},"\u002F\u002F cache miss → store\n",[33,1136,1137,1140],{"class":35,"line":355},[33,1138,1139],{"class":65},"        return",[33,1141,1142],{"class":73}," result;\n",[33,1144,1145],{"class":35,"line":361},[33,1146,817],{"class":73},[33,1148,1149],{"class":35,"line":367},[33,1150,163],{"class":73},[33,1152,1153,1155,1158,1160,1162,1165,1168,1170,1173,1176,1178,1181,1183,1186,1188,1191,1194,1196,1198,1201,1203,1205,1207,1209],{"class":35,"line":373},[33,1154,174],{"class":65},[33,1156,1157],{"class":69}," slowFib",[33,1159,87],{"class":65},[33,1161,417],{"class":73},[33,1163,1164],{"class":591},"n",[33,1166,1167],{"class":73},") ",[33,1169,457],{"class":65},[33,1171,1172],{"class":73}," n ",[33,1174,1175],{"class":65},"\u003C=",[33,1177,232],{"class":83},[33,1179,1180],{"class":65}," ?",[33,1182,1172],{"class":73},[33,1184,1185],{"class":65},":",[33,1187,1157],{"class":69},[33,1189,1190],{"class":73},"(n ",[33,1192,1193],{"class":65},"-",[33,1195,232],{"class":83},[33,1197,1167],{"class":73},[33,1199,1200],{"class":65},"+",[33,1202,1157],{"class":69},[33,1204,1190],{"class":73},[33,1206,1193],{"class":65},[33,1208,259],{"class":83},[33,1210,471],{"class":73},[33,1212,1213,1215,1218,1220,1222],{"class":35,"line":379},[33,1214,174],{"class":65},[33,1216,1217],{"class":83}," fastFib",[33,1219,87],{"class":65},[33,1221,1004],{"class":69},[33,1223,1224],{"class":73},"(slowFib);\n",[33,1226,1227,1230],{"class":35,"line":385},[33,1228,1229],{"class":39},"\u002F\u002F fastFib(40);",[33,1231,1232],{"class":39},"  \u002F\u002F fast (cached) — O(n) instead of O(2^n)\n",[15,1234,1236],{"id":1235},"closure-induced-memory-leaks","Closure-Induced Memory Leaks",[20,1238,1239],{"language":22},[24,1240,1242],{"className":26,"code":1241,"language":22,"meta":28,"style":28},"\u002F\u002F ── Closures keep captured variables alive — can cause memory leaks ──\nfunction createLeak() {\n    const hugeArray = new Array(1_000_000).fill(\"*\");  \u002F\u002F 1M elements (8MB)\n    return {\n        \u002F\u002F Only return a function that uses `length` — but the ENTIRE array is captured!\n        getLength: () => hugeArray.length,\n    };\n}\nconst leak = createLeak();  \u002F\u002F hugeArray stays alive as long as `leak` exists\nleak.getLength();  \u002F\u002F 1000000\n\u002F\u002F The entire 1M array is kept alive because the closure captures it.\n\u002F\u002F Even though we only use `.length`, the engine keeps the whole array.\n\n\u002F\u002F ✅ CORRECT — extract what you need, let the rest be garbage collected\nfunction createSafe() {\n    const hugeArray = new Array(1_000_000).fill(\"*\");\n    const length = hugeArray.length;  \u002F\u002F extract the value\n    \u002F\u002F hugeArray can now be GC'd (no longer referenced by the closure)\n    return {\n        getLength: () => length,  \u002F\u002F closure captures `length` (a number), not `hugeArray`\n    };\n}\nconst safe = createSafe();  \u002F\u002F hugeArray is GC'd after createSafe returns\nsafe.getLength();  \u002F\u002F 1000000 (only `length` is kept alive — a number, not the array)\n\n\u002F\u002F ── DOM event handler leak (old pattern) ──\nfunction setupHandler() {\n    const element = document.getElementById(\"button\");\n    const heavyData = fetchHeavyData();  \u002F\u002F large object\n\n    element.addEventListener(\"click\", () => {\n        console.log(heavyData);  \u002F\u002F closure captures heavyData → can't be GC'd\n    });\n    \u002F\u002F Even if element is removed from the DOM, the listener (and heavyData) stays alive\n    \u002F\u002F until the listener is removed or the element is GC'd.\n}\n\u002F\u002F ✅ Fix: remove the listener when no longer needed (or use AbortController)\n",[30,1243,1244,1249,1258,1294,1300,1305,1324,1328,1332,1348,1361,1366,1371,1375,1380,1389,1415,1433,1438,1444,1458,1462,1466,1482,1494,1498,1503,1512,1534,1551,1555,1575,1587,1592,1597,1602,1606],{"__ignoreMap":28},[33,1245,1246],{"class":35,"line":36},[33,1247,1248],{"class":39},"\u002F\u002F ── Closures keep captured variables alive — can cause memory leaks ──\n",[33,1250,1251,1253,1256],{"class":35,"line":43},[33,1252,66],{"class":65},[33,1254,1255],{"class":69}," createLeak",[33,1257,74],{"class":73},[33,1259,1260,1262,1265,1267,1269,1272,1274,1277,1280,1283,1285,1288,1291],{"class":35,"line":49},[33,1261,80],{"class":65},[33,1263,1264],{"class":83}," hugeArray",[33,1266,87],{"class":65},[33,1268,1023],{"class":65},[33,1270,1271],{"class":69}," Array",[33,1273,588],{"class":73},[33,1275,1276],{"class":83},"1_000_000",[33,1278,1279],{"class":73},").",[33,1281,1282],{"class":69},"fill",[33,1284,588],{"class":73},[33,1286,1287],{"class":90},"\"*\"",[33,1289,1290],{"class":73},");  ",[33,1292,1293],{"class":39},"\u002F\u002F 1M elements (8MB)\n",[33,1295,1296,1298],{"class":35,"line":55},[33,1297,151],{"class":65},[33,1299,749],{"class":73},[33,1301,1302],{"class":35,"line":62},[33,1303,1304],{"class":39},"        \u002F\u002F Only return a function that uses `length` — but the ENTIRE array is captured!\n",[33,1306,1307,1310,1313,1315,1318,1321],{"class":35,"line":77},[33,1308,1309],{"class":69},"        getLength",[33,1311,1312],{"class":73},": () ",[33,1314,457],{"class":65},[33,1316,1317],{"class":73}," hugeArray.",[33,1319,1320],{"class":83},"length",[33,1322,1323],{"class":73},",\n",[33,1325,1326],{"class":35,"line":100},[33,1327,817],{"class":73},[33,1329,1330],{"class":35,"line":105},[33,1331,163],{"class":73},[33,1333,1334,1336,1339,1341,1343,1345],{"class":35,"line":116},[33,1335,174],{"class":65},[33,1337,1338],{"class":83}," leak",[33,1340,87],{"class":65},[33,1342,1255],{"class":69},[33,1344,184],{"class":73},[33,1346,1347],{"class":39},"\u002F\u002F hugeArray stays alive as long as `leak` exists\n",[33,1349,1350,1353,1356,1358],{"class":35,"line":122},[33,1351,1352],{"class":73},"leak.",[33,1354,1355],{"class":69},"getLength",[33,1357,184],{"class":73},[33,1359,1360],{"class":39},"\u002F\u002F 1000000\n",[33,1362,1363],{"class":35,"line":137},[33,1364,1365],{"class":39},"\u002F\u002F The entire 1M array is kept alive because the closure captures it.\n",[33,1367,1368],{"class":35,"line":143},[33,1369,1370],{"class":39},"\u002F\u002F Even though we only use `.length`, the engine keeps the whole array.\n",[33,1372,1373],{"class":35,"line":148},[33,1374,59],{"emptyLinePlaceholder":58},[33,1376,1377],{"class":35,"line":160},[33,1378,1379],{"class":39},"\u002F\u002F ✅ CORRECT — extract what you need, let the rest be garbage collected\n",[33,1381,1382,1384,1387],{"class":35,"line":166},[33,1383,66],{"class":65},[33,1385,1386],{"class":69}," createSafe",[33,1388,74],{"class":73},[33,1390,1391,1393,1395,1397,1399,1401,1403,1405,1407,1409,1411,1413],{"class":35,"line":171},[33,1392,80],{"class":65},[33,1394,1264],{"class":83},[33,1396,87],{"class":65},[33,1398,1023],{"class":65},[33,1400,1271],{"class":69},[33,1402,588],{"class":73},[33,1404,1276],{"class":83},[33,1406,1279],{"class":73},[33,1408,1282],{"class":69},[33,1410,588],{"class":73},[33,1412,1287],{"class":90},[33,1414,471],{"class":73},[33,1416,1417,1419,1422,1424,1426,1428,1430],{"class":35,"line":190},[33,1418,80],{"class":65},[33,1420,1421],{"class":83}," length",[33,1423,87],{"class":65},[33,1425,1317],{"class":73},[33,1427,1320],{"class":83},[33,1429,94],{"class":73},[33,1431,1432],{"class":39},"\u002F\u002F extract the value\n",[33,1434,1435],{"class":35,"line":201},[33,1436,1437],{"class":39},"    \u002F\u002F hugeArray can now be GC'd (no longer referenced by the closure)\n",[33,1439,1440,1442],{"class":35,"line":206},[33,1441,151],{"class":65},[33,1443,749],{"class":73},[33,1445,1446,1448,1450,1452,1455],{"class":35,"line":212},[33,1447,1309],{"class":69},[33,1449,1312],{"class":73},[33,1451,457],{"class":65},[33,1453,1454],{"class":73}," length,  ",[33,1456,1457],{"class":39},"\u002F\u002F closure captures `length` (a number), not `hugeArray`\n",[33,1459,1460],{"class":35,"line":222},[33,1461,817],{"class":73},[33,1463,1464],{"class":35,"line":238},[33,1465,163],{"class":73},[33,1467,1468,1470,1473,1475,1477,1479],{"class":35,"line":248},[33,1469,174],{"class":65},[33,1471,1472],{"class":83}," safe",[33,1474,87],{"class":65},[33,1476,1386],{"class":69},[33,1478,184],{"class":73},[33,1480,1481],{"class":39},"\u002F\u002F hugeArray is GC'd after createSafe returns\n",[33,1483,1484,1487,1489,1491],{"class":35,"line":264},[33,1485,1486],{"class":73},"safe.",[33,1488,1355],{"class":69},[33,1490,184],{"class":73},[33,1492,1493],{"class":39},"\u002F\u002F 1000000 (only `length` is kept alive — a number, not the array)\n",[33,1495,1496],{"class":35,"line":275},[33,1497,59],{"emptyLinePlaceholder":58},[33,1499,1500],{"class":35,"line":291},[33,1501,1502],{"class":39},"\u002F\u002F ── DOM event handler leak (old pattern) ──\n",[33,1504,1505,1507,1510],{"class":35,"line":297},[33,1506,66],{"class":65},[33,1508,1509],{"class":69}," setupHandler",[33,1511,74],{"class":73},[33,1513,1514,1516,1519,1521,1524,1527,1529,1532],{"class":35,"line":303},[33,1515,80],{"class":65},[33,1517,1518],{"class":83}," element",[33,1520,87],{"class":65},[33,1522,1523],{"class":73}," document.",[33,1525,1526],{"class":69},"getElementById",[33,1528,588],{"class":73},[33,1530,1531],{"class":90},"\"button\"",[33,1533,471],{"class":73},[33,1535,1536,1538,1541,1543,1546,1548],{"class":35,"line":317},[33,1537,80],{"class":65},[33,1539,1540],{"class":83}," heavyData",[33,1542,87],{"class":65},[33,1544,1545],{"class":69}," fetchHeavyData",[33,1547,184],{"class":73},[33,1549,1550],{"class":39},"\u002F\u002F large object\n",[33,1552,1553],{"class":35,"line":323},[33,1554,59],{"emptyLinePlaceholder":58},[33,1556,1557,1560,1563,1565,1568,1571,1573],{"class":35,"line":332},[33,1558,1559],{"class":73},"    element.",[33,1561,1562],{"class":69},"addEventListener",[33,1564,588],{"class":73},[33,1566,1567],{"class":90},"\"click\"",[33,1569,1570],{"class":73},", () ",[33,1572,457],{"class":65},[33,1574,749],{"class":73},[33,1576,1577,1579,1581,1584],{"class":35,"line":337},[33,1578,125],{"class":73},[33,1580,128],{"class":69},[33,1582,1583],{"class":73},"(heavyData);  ",[33,1585,1586],{"class":39},"\u002F\u002F closure captures heavyData → can't be GC'd\n",[33,1588,1589],{"class":35,"line":345},[33,1590,1591],{"class":73},"    });\n",[33,1593,1594],{"class":35,"line":350},[33,1595,1596],{"class":39},"    \u002F\u002F Even if element is removed from the DOM, the listener (and heavyData) stays alive\n",[33,1598,1599],{"class":35,"line":355},[33,1600,1601],{"class":39},"    \u002F\u002F until the listener is removed or the element is GC'd.\n",[33,1603,1604],{"class":35,"line":361},[33,1605,163],{"class":73},[33,1607,1608],{"class":35,"line":367},[33,1609,1610],{"class":39},"\u002F\u002F ✅ Fix: remove the listener when no longer needed (or use AbortController)\n",[15,1612,1614],{"id":1613},"production-pattern-once-and-memoize","Production Pattern: Once and Memoize",[20,1616,1617],{"language":22},[24,1618,1620],{"className":26,"code":1619,"language":22,"meta":28,"style":28},"\u002F\u002F ── once: ensure a function runs only once (cached result) ──\nfunction once(fn) {\n    let called = false;\n    let result;\n    return function(...args) {\n        if (called) return result;  \u002F\u002F return cached result on subsequent calls\n        called = true;\n        result = fn.apply(this, args);\n        return result;\n    };\n}\nconst init = once(() => { console.log(\"initializing\"); return \"done\"; });\ninit();  \u002F\u002F logs \"initializing\", returns \"done\"\ninit();  \u002F\u002F returns \"done\" (cached, no log)\n\n\u002F\u002F ── debounce: delay execution until calls stop (closure over timeout) ──\nfunction debounce(fn, delay) {\n    let timer;  \u002F\u002F closure-captured timer ID\n    return function(...args) {\n        clearTimeout(timer);  \u002F\u002F clear previous timer (if any)\n        timer = setTimeout(() => fn.apply(this, args), delay);\n    };\n}\nconst search = debounce(query => console.log(\"searching:\", query), 300);\n\u002F\u002F search(\"a\"); search(\"ab\"); search(\"abc\");\n\u002F\u002F Only \"searching: abc\" runs (after 300ms of no typing)\n\n\u002F\u002F ── throttle: limit execution rate (closure over last-run time) ──\nfunction throttle(fn, limit) {\n    let lastRun = 0;\n    return function(...args) {\n        const now = Date.now();\n        if (now - lastRun >= limit) {\n            lastRun = now;\n            fn.apply(this, args);\n        }\n    };\n}\nconst onScroll = throttle(() => console.log(\"scrolled\"), 100);\n\u002F\u002F window.addEventListener(\"scroll\", onScroll);  \u002F\u002F runs at most every 100ms\n",[30,1621,1622,1627,1640,1654,1660,1674,1689,1701,1718,1724,1728,1732,1768,1778,1787,1791,1796,1815,1825,1839,1850,1875,1879,1883,1919,1924,1929,1933,1938,1956,1969,1983,2000,2017,2027,2040,2044,2048,2052,2083],{"__ignoreMap":28},[33,1623,1624],{"class":35,"line":36},[33,1625,1626],{"class":39},"\u002F\u002F ── once: ensure a function runs only once (cached result) ──\n",[33,1628,1629,1631,1634,1636,1638],{"class":35,"line":43},[33,1630,66],{"class":65},[33,1632,1633],{"class":69}," once",[33,1635,588],{"class":73},[33,1637,1009],{"class":591},[33,1639,446],{"class":73},[33,1641,1642,1644,1647,1649,1652],{"class":35,"line":49},[33,1643,727],{"class":65},[33,1645,1646],{"class":73}," called ",[33,1648,426],{"class":65},[33,1650,1651],{"class":83}," false",[33,1653,235],{"class":73},[33,1655,1656,1658],{"class":35,"line":55},[33,1657,727],{"class":65},[33,1659,1142],{"class":73},[33,1661,1662,1664,1666,1668,1670,1672],{"class":35,"line":62},[33,1663,151],{"class":65},[33,1665,1038],{"class":65},[33,1667,588],{"class":73},[33,1669,1043],{"class":65},[33,1671,1046],{"class":591},[33,1673,446],{"class":73},[33,1675,1676,1678,1681,1683,1686],{"class":35,"line":77},[33,1677,1074],{"class":65},[33,1679,1680],{"class":73}," (called) ",[33,1682,760],{"class":65},[33,1684,1685],{"class":73}," result;  ",[33,1687,1688],{"class":39},"\u002F\u002F return cached result on subsequent calls\n",[33,1690,1691,1694,1696,1699],{"class":35,"line":100},[33,1692,1693],{"class":73},"        called ",[33,1695,426],{"class":65},[33,1697,1698],{"class":83}," true",[33,1700,235],{"class":73},[33,1702,1703,1706,1708,1710,1712,1714,1716],{"class":35,"line":105},[33,1704,1705],{"class":73},"        result ",[33,1707,426],{"class":65},[33,1709,1109],{"class":73},[33,1711,1112],{"class":69},[33,1713,588],{"class":73},[33,1715,1117],{"class":83},[33,1717,1120],{"class":73},[33,1719,1720,1722],{"class":35,"line":116},[33,1721,1139],{"class":65},[33,1723,1142],{"class":73},[33,1725,1726],{"class":35,"line":122},[33,1727,817],{"class":73},[33,1729,1730],{"class":35,"line":137},[33,1731,163],{"class":73},[33,1733,1734,1736,1739,1741,1743,1745,1747,1750,1752,1754,1757,1760,1762,1765],{"class":35,"line":143},[33,1735,174],{"class":65},[33,1737,1738],{"class":83}," init",[33,1740,87],{"class":65},[33,1742,1633],{"class":69},[33,1744,454],{"class":73},[33,1746,457],{"class":65},[33,1748,1749],{"class":73}," { console.",[33,1751,128],{"class":69},[33,1753,588],{"class":73},[33,1755,1756],{"class":90},"\"initializing\"",[33,1758,1759],{"class":73},"); ",[33,1761,760],{"class":65},[33,1763,1764],{"class":90}," \"done\"",[33,1766,1767],{"class":73},"; });\n",[33,1769,1770,1773,1775],{"class":35,"line":148},[33,1771,1772],{"class":69},"init",[33,1774,184],{"class":73},[33,1776,1777],{"class":39},"\u002F\u002F logs \"initializing\", returns \"done\"\n",[33,1779,1780,1782,1784],{"class":35,"line":160},[33,1781,1772],{"class":69},[33,1783,184],{"class":73},[33,1785,1786],{"class":39},"\u002F\u002F returns \"done\" (cached, no log)\n",[33,1788,1789],{"class":35,"line":166},[33,1790,59],{"emptyLinePlaceholder":58},[33,1792,1793],{"class":35,"line":171},[33,1794,1795],{"class":39},"\u002F\u002F ── debounce: delay execution until calls stop (closure over timeout) ──\n",[33,1797,1798,1800,1803,1805,1807,1810,1813],{"class":35,"line":190},[33,1799,66],{"class":65},[33,1801,1802],{"class":69}," debounce",[33,1804,588],{"class":73},[33,1806,1009],{"class":591},[33,1808,1809],{"class":73},", ",[33,1811,1812],{"class":591},"delay",[33,1814,446],{"class":73},[33,1816,1817,1819,1822],{"class":35,"line":201},[33,1818,727],{"class":65},[33,1820,1821],{"class":73}," timer;  ",[33,1823,1824],{"class":39},"\u002F\u002F closure-captured timer ID\n",[33,1826,1827,1829,1831,1833,1835,1837],{"class":35,"line":206},[33,1828,151],{"class":65},[33,1830,1038],{"class":65},[33,1832,588],{"class":73},[33,1834,1043],{"class":65},[33,1836,1046],{"class":591},[33,1838,446],{"class":73},[33,1840,1841,1844,1847],{"class":35,"line":212},[33,1842,1843],{"class":69},"        clearTimeout",[33,1845,1846],{"class":73},"(timer);  ",[33,1848,1849],{"class":39},"\u002F\u002F clear previous timer (if any)\n",[33,1851,1852,1855,1857,1860,1862,1864,1866,1868,1870,1872],{"class":35,"line":222},[33,1853,1854],{"class":73},"        timer ",[33,1856,426],{"class":65},[33,1858,1859],{"class":69}," setTimeout",[33,1861,454],{"class":73},[33,1863,457],{"class":65},[33,1865,1109],{"class":73},[33,1867,1112],{"class":69},[33,1869,588],{"class":73},[33,1871,1117],{"class":83},[33,1873,1874],{"class":73},", args), delay);\n",[33,1876,1877],{"class":35,"line":238},[33,1878,817],{"class":73},[33,1880,1881],{"class":35,"line":248},[33,1882,163],{"class":73},[33,1884,1885,1887,1890,1892,1894,1896,1899,1902,1904,1906,1908,1911,1914,1917],{"class":35,"line":264},[33,1886,174],{"class":65},[33,1888,1889],{"class":83}," search",[33,1891,87],{"class":65},[33,1893,1802],{"class":69},[33,1895,588],{"class":73},[33,1897,1898],{"class":591},"query",[33,1900,1901],{"class":65}," =>",[33,1903,460],{"class":73},[33,1905,128],{"class":69},[33,1907,588],{"class":73},[33,1909,1910],{"class":90},"\"searching:\"",[33,1912,1913],{"class":73},", query), ",[33,1915,1916],{"class":83},"300",[33,1918,471],{"class":73},[33,1920,1921],{"class":35,"line":275},[33,1922,1923],{"class":39},"\u002F\u002F search(\"a\"); search(\"ab\"); search(\"abc\");\n",[33,1925,1926],{"class":35,"line":291},[33,1927,1928],{"class":39},"\u002F\u002F Only \"searching: abc\" runs (after 300ms of no typing)\n",[33,1930,1931],{"class":35,"line":297},[33,1932,59],{"emptyLinePlaceholder":58},[33,1934,1935],{"class":35,"line":303},[33,1936,1937],{"class":39},"\u002F\u002F ── throttle: limit execution rate (closure over last-run time) ──\n",[33,1939,1940,1942,1945,1947,1949,1951,1954],{"class":35,"line":317},[33,1941,66],{"class":65},[33,1943,1944],{"class":69}," throttle",[33,1946,588],{"class":73},[33,1948,1009],{"class":591},[33,1950,1809],{"class":73},[33,1952,1953],{"class":591},"limit",[33,1955,446],{"class":73},[33,1957,1958,1960,1963,1965,1967],{"class":35,"line":323},[33,1959,727],{"class":65},[33,1961,1962],{"class":73}," lastRun ",[33,1964,426],{"class":65},[33,1966,429],{"class":83},[33,1968,235],{"class":73},[33,1970,1971,1973,1975,1977,1979,1981],{"class":35,"line":332},[33,1972,151],{"class":65},[33,1974,1038],{"class":65},[33,1976,588],{"class":73},[33,1978,1043],{"class":65},[33,1980,1046],{"class":591},[33,1982,446],{"class":73},[33,1984,1985,1987,1990,1992,1995,1998],{"class":35,"line":337},[33,1986,251],{"class":65},[33,1988,1989],{"class":83}," now",[33,1991,87],{"class":65},[33,1993,1994],{"class":73}," Date.",[33,1996,1997],{"class":69},"now",[33,1999,329],{"class":73},[33,2001,2002,2004,2007,2009,2011,2014],{"class":35,"line":345},[33,2003,1074],{"class":65},[33,2005,2006],{"class":73}," (now ",[33,2008,1193],{"class":65},[33,2010,1962],{"class":73},[33,2012,2013],{"class":65},">=",[33,2015,2016],{"class":73}," limit) {\n",[33,2018,2019,2022,2024],{"class":35,"line":350},[33,2020,2021],{"class":73},"            lastRun ",[33,2023,426],{"class":65},[33,2025,2026],{"class":73}," now;\n",[33,2028,2029,2032,2034,2036,2038],{"class":35,"line":355},[33,2030,2031],{"class":73},"            fn.",[33,2033,1112],{"class":69},[33,2035,588],{"class":73},[33,2037,1117],{"class":83},[33,2039,1120],{"class":73},[33,2041,2042],{"class":35,"line":361},[33,2043,320],{"class":73},[33,2045,2046],{"class":35,"line":367},[33,2047,817],{"class":73},[33,2049,2050],{"class":35,"line":373},[33,2051,163],{"class":73},[33,2053,2054,2056,2059,2061,2063,2065,2067,2069,2071,2073,2076,2079,2081],{"class":35,"line":379},[33,2055,174],{"class":65},[33,2057,2058],{"class":83}," onScroll",[33,2060,87],{"class":65},[33,2062,1944],{"class":69},[33,2064,454],{"class":73},[33,2066,457],{"class":65},[33,2068,460],{"class":73},[33,2070,128],{"class":69},[33,2072,588],{"class":73},[33,2074,2075],{"class":90},"\"scrolled\"",[33,2077,2078],{"class":73},"), ",[33,2080,940],{"class":83},[33,2082,471],{"class":73},[33,2084,2085,2088],{"class":35,"line":385},[33,2086,2087],{"class":39},"\u002F\u002F window.addEventListener(\"scroll\", onScroll);",[33,2089,2090],{"class":39},"  \u002F\u002F runs at most every 100ms\n",[15,2092,2094],{"id":2093},"tips-tricks","💡 Tips & Tricks",[20,2096,2097],{"language":22},[24,2098,2100],{"className":26,"code":2099,"language":22,"meta":28,"style":28},"\u002F\u002F ── Closure for partial application (curry) ──\nconst curry = (fn) => {\n    return function curried(...args) {\n        if (args.length >= fn.length) {  \u002F\u002F enough args → call\n            return fn.apply(this, args);\n        }\n        return (...more) => curried(...args, ...more);  \u002F\u002F not enough → return new closure\n    };\n};\nconst add = curry((a, b, c) => a + b + c);\nadd(1)(2)(3);   \u002F\u002F 6 (fully curried)\nadd(1, 2)(3);   \u002F\u002F 6 (partial application)\nadd(1, 2, 3);   \u002F\u002F 6 (all at once)\n\n\u002F\u002F ── Closure for a state machine ──\nfunction createTrafficLight() {\n    const states = [\"red\", \"green\", \"yellow\"];\n    let index = 0;\n    return {\n        next() { index = (index + 1) % states.length; return states[index]; },\n        current() { return states[index]; },\n    };\n}\n\n\u002F\u002F ── Closure for event unregistration (cleanup) ──\nfunction onEvent(element, event, handler) {\n    element.addEventListener(event, handler);\n    return () => element.removeEventListener(event, handler);  \u002F\u002F return cleanup function\n}\nconst cleanup = onEvent(button, \"click\", () => console.log(\"clicked\"));\n\u002F\u002F Later: cleanup();  \u002F\u002F removes the listener (closure captures element, event, handler)\n",[30,2101,2102,2107,2126,2143,2164,2179,2183,2215,2219,2224,2268,2295,2316,2337,2341,2346,2355,2383,2396,2402,2437,2448,2452,2456,2460,2465,2489,2498,2519,2523,2555],{"__ignoreMap":28},[33,2103,2104],{"class":35,"line":36},[33,2105,2106],{"class":39},"\u002F\u002F ── Closure for partial application (curry) ──\n",[33,2108,2109,2111,2114,2116,2118,2120,2122,2124],{"class":35,"line":43},[33,2110,174],{"class":65},[33,2112,2113],{"class":69}," curry",[33,2115,87],{"class":65},[33,2117,417],{"class":73},[33,2119,1009],{"class":591},[33,2121,1167],{"class":73},[33,2123,457],{"class":65},[33,2125,749],{"class":73},[33,2127,2128,2130,2132,2135,2137,2139,2141],{"class":35,"line":49},[33,2129,151],{"class":65},[33,2131,1038],{"class":65},[33,2133,2134],{"class":69}," curried",[33,2136,588],{"class":73},[33,2138,1043],{"class":65},[33,2140,1046],{"class":591},[33,2142,446],{"class":73},[33,2144,2145,2147,2150,2152,2155,2157,2159,2161],{"class":35,"line":55},[33,2146,1074],{"class":65},[33,2148,2149],{"class":73}," (args.",[33,2151,1320],{"class":83},[33,2153,2154],{"class":65}," >=",[33,2156,1109],{"class":73},[33,2158,1320],{"class":83},[33,2160,595],{"class":73},[33,2162,2163],{"class":39},"\u002F\u002F enough args → call\n",[33,2165,2166,2169,2171,2173,2175,2177],{"class":35,"line":62},[33,2167,2168],{"class":65},"            return",[33,2170,1109],{"class":73},[33,2172,1112],{"class":69},[33,2174,588],{"class":73},[33,2176,1117],{"class":83},[33,2178,1120],{"class":73},[33,2180,2181],{"class":35,"line":77},[33,2182,320],{"class":73},[33,2184,2185,2187,2189,2191,2194,2196,2198,2200,2202,2204,2207,2209,2212],{"class":35,"line":100},[33,2186,1139],{"class":65},[33,2188,417],{"class":73},[33,2190,1043],{"class":65},[33,2192,2193],{"class":591},"more",[33,2195,1167],{"class":73},[33,2197,457],{"class":65},[33,2199,2134],{"class":69},[33,2201,588],{"class":73},[33,2203,1043],{"class":65},[33,2205,2206],{"class":73},"args, ",[33,2208,1043],{"class":65},[33,2210,2211],{"class":73},"more);  ",[33,2213,2214],{"class":39},"\u002F\u002F not enough → return new closure\n",[33,2216,2217],{"class":35,"line":105},[33,2218,817],{"class":73},[33,2220,2221],{"class":35,"line":116},[33,2222,2223],{"class":73},"};\n",[33,2225,2226,2228,2231,2233,2235,2238,2241,2243,2246,2248,2251,2253,2255,2258,2260,2263,2265],{"class":35,"line":122},[33,2227,174],{"class":65},[33,2229,2230],{"class":83}," add",[33,2232,87],{"class":65},[33,2234,2113],{"class":69},[33,2236,2237],{"class":73},"((",[33,2239,2240],{"class":591},"a",[33,2242,1809],{"class":73},[33,2244,2245],{"class":591},"b",[33,2247,1809],{"class":73},[33,2249,2250],{"class":591},"c",[33,2252,1167],{"class":73},[33,2254,457],{"class":65},[33,2256,2257],{"class":73}," a ",[33,2259,1200],{"class":65},[33,2261,2262],{"class":73}," b ",[33,2264,1200],{"class":65},[33,2266,2267],{"class":73}," c);\n",[33,2269,2270,2273,2275,2278,2281,2284,2286,2289,2292],{"class":35,"line":137},[33,2271,2272],{"class":69},"add",[33,2274,588],{"class":73},[33,2276,2277],{"class":83},"1",[33,2279,2280],{"class":73},")(",[33,2282,2283],{"class":83},"2",[33,2285,2280],{"class":73},[33,2287,2288],{"class":83},"3",[33,2290,2291],{"class":73},");   ",[33,2293,2294],{"class":39},"\u002F\u002F 6 (fully curried)\n",[33,2296,2297,2299,2301,2303,2305,2307,2309,2311,2313],{"class":35,"line":143},[33,2298,2272],{"class":69},[33,2300,588],{"class":73},[33,2302,2277],{"class":83},[33,2304,1809],{"class":73},[33,2306,2283],{"class":83},[33,2308,2280],{"class":73},[33,2310,2288],{"class":83},[33,2312,2291],{"class":73},[33,2314,2315],{"class":39},"\u002F\u002F 6 (partial application)\n",[33,2317,2318,2320,2322,2324,2326,2328,2330,2332,2334],{"class":35,"line":148},[33,2319,2272],{"class":69},[33,2321,588],{"class":73},[33,2323,2277],{"class":83},[33,2325,1809],{"class":73},[33,2327,2283],{"class":83},[33,2329,1809],{"class":73},[33,2331,2288],{"class":83},[33,2333,2291],{"class":73},[33,2335,2336],{"class":39},"\u002F\u002F 6 (all at once)\n",[33,2338,2339],{"class":35,"line":160},[33,2340,59],{"emptyLinePlaceholder":58},[33,2342,2343],{"class":35,"line":166},[33,2344,2345],{"class":39},"\u002F\u002F ── Closure for a state machine ──\n",[33,2347,2348,2350,2353],{"class":35,"line":171},[33,2349,66],{"class":65},[33,2351,2352],{"class":69}," createTrafficLight",[33,2354,74],{"class":73},[33,2356,2357,2359,2362,2364,2367,2370,2372,2375,2377,2380],{"class":35,"line":190},[33,2358,80],{"class":65},[33,2360,2361],{"class":83}," states",[33,2363,87],{"class":65},[33,2365,2366],{"class":73}," [",[33,2368,2369],{"class":90},"\"red\"",[33,2371,1809],{"class":73},[33,2373,2374],{"class":90},"\"green\"",[33,2376,1809],{"class":73},[33,2378,2379],{"class":90},"\"yellow\"",[33,2381,2382],{"class":73},"];\n",[33,2384,2385,2387,2390,2392,2394],{"class":35,"line":201},[33,2386,727],{"class":65},[33,2388,2389],{"class":73}," index ",[33,2391,426],{"class":65},[33,2393,429],{"class":83},[33,2395,235],{"class":73},[33,2397,2398,2400],{"class":35,"line":206},[33,2399,151],{"class":65},[33,2401,749],{"class":73},[33,2403,2404,2407,2410,2412,2415,2417,2419,2421,2424,2427,2429,2432,2434],{"class":35,"line":212},[33,2405,2406],{"class":69},"        next",[33,2408,2409],{"class":73},"() { index ",[33,2411,426],{"class":65},[33,2413,2414],{"class":73}," (index ",[33,2416,1200],{"class":65},[33,2418,232],{"class":83},[33,2420,1167],{"class":73},[33,2422,2423],{"class":65},"%",[33,2425,2426],{"class":73}," states.",[33,2428,1320],{"class":83},[33,2430,2431],{"class":73},"; ",[33,2433,760],{"class":65},[33,2435,2436],{"class":73}," states[index]; },\n",[33,2438,2439,2442,2444,2446],{"class":35,"line":222},[33,2440,2441],{"class":69},"        current",[33,2443,757],{"class":73},[33,2445,760],{"class":65},[33,2447,2436],{"class":73},[33,2449,2450],{"class":35,"line":238},[33,2451,817],{"class":73},[33,2453,2454],{"class":35,"line":248},[33,2455,163],{"class":73},[33,2457,2458],{"class":35,"line":264},[33,2459,59],{"emptyLinePlaceholder":58},[33,2461,2462],{"class":35,"line":275},[33,2463,2464],{"class":39},"\u002F\u002F ── Closure for event unregistration (cleanup) ──\n",[33,2466,2467,2469,2472,2474,2477,2479,2482,2484,2487],{"class":35,"line":291},[33,2468,66],{"class":65},[33,2470,2471],{"class":69}," onEvent",[33,2473,588],{"class":73},[33,2475,2476],{"class":591},"element",[33,2478,1809],{"class":73},[33,2480,2481],{"class":591},"event",[33,2483,1809],{"class":73},[33,2485,2486],{"class":591},"handler",[33,2488,446],{"class":73},[33,2490,2491,2493,2495],{"class":35,"line":297},[33,2492,1559],{"class":73},[33,2494,1562],{"class":69},[33,2496,2497],{"class":73},"(event, handler);\n",[33,2499,2500,2502,2505,2507,2510,2513,2516],{"class":35,"line":303},[33,2501,151],{"class":65},[33,2503,2504],{"class":73}," () ",[33,2506,457],{"class":65},[33,2508,2509],{"class":73}," element.",[33,2511,2512],{"class":69},"removeEventListener",[33,2514,2515],{"class":73},"(event, handler);  ",[33,2517,2518],{"class":39},"\u002F\u002F return cleanup function\n",[33,2520,2521],{"class":35,"line":317},[33,2522,163],{"class":73},[33,2524,2525,2527,2530,2532,2534,2537,2539,2541,2543,2545,2547,2549,2552],{"class":35,"line":323},[33,2526,174],{"class":65},[33,2528,2529],{"class":83}," cleanup",[33,2531,87],{"class":65},[33,2533,2471],{"class":69},[33,2535,2536],{"class":73},"(button, ",[33,2538,1567],{"class":90},[33,2540,1570],{"class":73},[33,2542,457],{"class":65},[33,2544,460],{"class":73},[33,2546,128],{"class":69},[33,2548,588],{"class":73},[33,2550,2551],{"class":90},"\"clicked\"",[33,2553,2554],{"class":73},"));\n",[33,2556,2557,2560],{"class":35,"line":332},[33,2558,2559],{"class":39},"\u002F\u002F Later: cleanup();",[33,2561,2562],{"class":39},"  \u002F\u002F removes the listener (closure captures element, event, handler)\n",[15,2564,2566],{"id":2565},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,2568,2569],{"language":22},[24,2570,2572],{"className":26,"code":2571,"language":22,"meta":28,"style":28},"\u002F\u002F ── Closures capture the VARIABLE, not the VALUE ──\nlet x = 1;\nconst getX = () => x;  \u002F\u002F captures the variable `x`, not the value 1\nx = 2;\nconsole.log(getX());  \u002F\u002F 2 (reflects the latest value of x)\n\n\u002F\u002F ── Closures capture by reference, not by copy ──\nlet obj = { count: 0 };\nconst getObj = () => obj;  \u002F\u002F captures the variable `obj`\nobj = { count: 99 };\nconsole.log(getObj());  \u002F\u002F { count: 99 } (new object — the variable was reassigned)\n\n\u002F\u002F ── `let` in loops creates a new binding per iteration (fixes the closure bug) ──\n\u002F\u002F `var` in loops: all closures share the same binding → classic bug (3, 3, 3)\n\u002F\u002F `let` in loops: each iteration has its own binding → correct (0, 1, 2)\n\n\u002F\u002F ── Closures can cause memory leaks (captured variables stay alive) ──\n\u002F\u002F If a closure captures a large object, the object stays alive as long as the closure exists.\n\u002F\u002F Fix: extract only what you need (primitives), let the large object be GC'd.\n\n\u002F\u002F ── Closures over `this` (save reference) ──\nconst obj = {\n    items: [],\n    init() {\n        const self = this;  \u002F\u002F save `this` reference for closures\n        document.querySelectorAll(\".item\").forEach(function(el) {\n            self.items.push(el);  \u002F\u002F closure captures `self` (not `this`)\n        });\n    },\n};\n\n\u002F\u002F ── Closures in async: captured values may change before the async callback runs ──\nfor (let i = 0; i \u003C 3; i++) {\n    setTimeout(() => console.log(i), 0);  \u002F\u002F ✓ let: 0, 1, 2 (each has own binding)\n}\nfor (var i = 0; i \u003C 3; i++) {\n    setTimeout(() => console.log(i), 0);  \u002F\u002F ✗ var: 3, 3, 3 (shared binding)\n}\n",[30,2573,2574,2579,2592,2611,2622,2638,2642,2647,2664,2683,2697,2713,2717,2722,2727,2732,2736,2741,2746,2751,2755,2760,2771,2776,2783,2800,2829,2843,2848,2853,2857,2861,2866,2892,2913,2917,2943,2964],{"__ignoreMap":28},[33,2575,2576],{"class":35,"line":36},[33,2577,2578],{"class":39},"\u002F\u002F ── Closures capture the VARIABLE, not the VALUE ──\n",[33,2580,2581,2583,2586,2588,2590],{"class":35,"line":43},[33,2582,498],{"class":65},[33,2584,2585],{"class":73}," x ",[33,2587,426],{"class":65},[33,2589,232],{"class":83},[33,2591,235],{"class":73},[33,2593,2594,2596,2599,2601,2603,2605,2608],{"class":35,"line":49},[33,2595,174],{"class":65},[33,2597,2598],{"class":69}," getX",[33,2600,87],{"class":65},[33,2602,2504],{"class":73},[33,2604,457],{"class":65},[33,2606,2607],{"class":73}," x;  ",[33,2609,2610],{"class":39},"\u002F\u002F captures the variable `x`, not the value 1\n",[33,2612,2613,2616,2618,2620],{"class":35,"line":55},[33,2614,2615],{"class":73},"x ",[33,2617,426],{"class":65},[33,2619,259],{"class":83},[33,2621,235],{"class":73},[33,2623,2624,2626,2628,2630,2633,2635],{"class":35,"line":62},[33,2625,872],{"class":73},[33,2627,128],{"class":69},[33,2629,588],{"class":73},[33,2631,2632],{"class":69},"getX",[33,2634,883],{"class":73},[33,2636,2637],{"class":39},"\u002F\u002F 2 (reflects the latest value of x)\n",[33,2639,2640],{"class":35,"line":77},[33,2641,59],{"emptyLinePlaceholder":58},[33,2643,2644],{"class":35,"line":100},[33,2645,2646],{"class":39},"\u002F\u002F ── Closures capture by reference, not by copy ──\n",[33,2648,2649,2651,2654,2656,2659,2661],{"class":35,"line":105},[33,2650,498],{"class":65},[33,2652,2653],{"class":73}," obj ",[33,2655,426],{"class":65},[33,2657,2658],{"class":73}," { count: ",[33,2660,468],{"class":83},[33,2662,2663],{"class":73}," };\n",[33,2665,2666,2668,2671,2673,2675,2677,2680],{"class":35,"line":116},[33,2667,174],{"class":65},[33,2669,2670],{"class":69}," getObj",[33,2672,87],{"class":65},[33,2674,2504],{"class":73},[33,2676,457],{"class":65},[33,2678,2679],{"class":73}," obj;  ",[33,2681,2682],{"class":39},"\u002F\u002F captures the variable `obj`\n",[33,2684,2685,2688,2690,2692,2695],{"class":35,"line":122},[33,2686,2687],{"class":73},"obj ",[33,2689,426],{"class":65},[33,2691,2658],{"class":73},[33,2693,2694],{"class":83},"99",[33,2696,2663],{"class":73},[33,2698,2699,2701,2703,2705,2708,2710],{"class":35,"line":137},[33,2700,872],{"class":73},[33,2702,128],{"class":69},[33,2704,588],{"class":73},[33,2706,2707],{"class":69},"getObj",[33,2709,883],{"class":73},[33,2711,2712],{"class":39},"\u002F\u002F { count: 99 } (new object — the variable was reassigned)\n",[33,2714,2715],{"class":35,"line":143},[33,2716,59],{"emptyLinePlaceholder":58},[33,2718,2719],{"class":35,"line":148},[33,2720,2721],{"class":39},"\u002F\u002F ── `let` in loops creates a new binding per iteration (fixes the closure bug) ──\n",[33,2723,2724],{"class":35,"line":160},[33,2725,2726],{"class":39},"\u002F\u002F `var` in loops: all closures share the same binding → classic bug (3, 3, 3)\n",[33,2728,2729],{"class":35,"line":166},[33,2730,2731],{"class":39},"\u002F\u002F `let` in loops: each iteration has its own binding → correct (0, 1, 2)\n",[33,2733,2734],{"class":35,"line":171},[33,2735,59],{"emptyLinePlaceholder":58},[33,2737,2738],{"class":35,"line":190},[33,2739,2740],{"class":39},"\u002F\u002F ── Closures can cause memory leaks (captured variables stay alive) ──\n",[33,2742,2743],{"class":35,"line":201},[33,2744,2745],{"class":39},"\u002F\u002F If a closure captures a large object, the object stays alive as long as the closure exists.\n",[33,2747,2748],{"class":35,"line":206},[33,2749,2750],{"class":39},"\u002F\u002F Fix: extract only what you need (primitives), let the large object be GC'd.\n",[33,2752,2753],{"class":35,"line":212},[33,2754,59],{"emptyLinePlaceholder":58},[33,2756,2757],{"class":35,"line":222},[33,2758,2759],{"class":39},"\u002F\u002F ── Closures over `this` (save reference) ──\n",[33,2761,2762,2764,2767,2769],{"class":35,"line":238},[33,2763,174],{"class":65},[33,2765,2766],{"class":83}," obj",[33,2768,87],{"class":65},[33,2770,749],{"class":73},[33,2772,2773],{"class":35,"line":248},[33,2774,2775],{"class":73},"    items: [],\n",[33,2777,2778,2781],{"class":35,"line":264},[33,2779,2780],{"class":69},"    init",[33,2782,74],{"class":73},[33,2784,2785,2787,2790,2792,2795,2797],{"class":35,"line":275},[33,2786,251],{"class":65},[33,2788,2789],{"class":83}," self",[33,2791,87],{"class":65},[33,2793,2794],{"class":83}," this",[33,2796,94],{"class":73},[33,2798,2799],{"class":39},"\u002F\u002F save `this` reference for closures\n",[33,2801,2802,2805,2808,2810,2813,2815,2818,2820,2822,2824,2827],{"class":35,"line":291},[33,2803,2804],{"class":73},"        document.",[33,2806,2807],{"class":69},"querySelectorAll",[33,2809,588],{"class":73},[33,2811,2812],{"class":90},"\".item\"",[33,2814,1279],{"class":73},[33,2816,2817],{"class":69},"forEach",[33,2819,588],{"class":73},[33,2821,66],{"class":65},[33,2823,588],{"class":73},[33,2825,2826],{"class":591},"el",[33,2828,446],{"class":73},[33,2830,2831,2834,2837,2840],{"class":35,"line":297},[33,2832,2833],{"class":73},"            self.items.",[33,2835,2836],{"class":69},"push",[33,2838,2839],{"class":73},"(el);  ",[33,2841,2842],{"class":39},"\u002F\u002F closure captures `self` (not `this`)\n",[33,2844,2845],{"class":35,"line":303},[33,2846,2847],{"class":73},"        });\n",[33,2849,2850],{"class":35,"line":317},[33,2851,2852],{"class":73},"    },\n",[33,2854,2855],{"class":35,"line":323},[33,2856,2223],{"class":73},[33,2858,2859],{"class":35,"line":332},[33,2860,59],{"emptyLinePlaceholder":58},[33,2862,2863],{"class":35,"line":337},[33,2864,2865],{"class":39},"\u002F\u002F ── Closures in async: captured values may change before the async callback runs ──\n",[33,2867,2868,2870,2872,2874,2876,2878,2880,2882,2884,2886,2888,2890],{"class":35,"line":345},[33,2869,414],{"class":65},[33,2871,417],{"class":73},[33,2873,498],{"class":65},[33,2875,423],{"class":73},[33,2877,426],{"class":65},[33,2879,429],{"class":83},[33,2881,432],{"class":73},[33,2883,435],{"class":65},[33,2885,286],{"class":83},[33,2887,440],{"class":73},[33,2889,443],{"class":65},[33,2891,446],{"class":73},[33,2893,2894,2896,2898,2900,2902,2904,2906,2908,2910],{"class":35,"line":350},[33,2895,451],{"class":69},[33,2897,454],{"class":73},[33,2899,457],{"class":65},[33,2901,460],{"class":73},[33,2903,128],{"class":69},[33,2905,465],{"class":73},[33,2907,468],{"class":83},[33,2909,1290],{"class":73},[33,2911,2912],{"class":39},"\u002F\u002F ✓ let: 0, 1, 2 (each has own binding)\n",[33,2914,2915],{"class":35,"line":355},[33,2916,163],{"class":73},[33,2918,2919,2921,2923,2925,2927,2929,2931,2933,2935,2937,2939,2941],{"class":35,"line":361},[33,2920,414],{"class":65},[33,2922,417],{"class":73},[33,2924,420],{"class":65},[33,2926,423],{"class":73},[33,2928,426],{"class":65},[33,2930,429],{"class":83},[33,2932,432],{"class":73},[33,2934,435],{"class":65},[33,2936,286],{"class":83},[33,2938,440],{"class":73},[33,2940,443],{"class":65},[33,2942,446],{"class":73},[33,2944,2945,2947,2949,2951,2953,2955,2957,2959,2961],{"class":35,"line":367},[33,2946,451],{"class":69},[33,2948,454],{"class":73},[33,2950,457],{"class":65},[33,2952,460],{"class":73},[33,2954,128],{"class":69},[33,2956,465],{"class":73},[33,2958,468],{"class":83},[33,2960,1290],{"class":73},[33,2962,2963],{"class":39},"\u002F\u002F ✗ var: 3, 3, 3 (shared binding)\n",[33,2965,2966],{"class":35,"line":373},[33,2967,163],{"class":73},[15,2969,2971],{"id":2970},"quick-quiz","🧠 Quick Quiz",[2973,2974,2975],"p",{},"What does this output?",[20,2977,2978],{"language":22},[24,2979,2981],{"className":26,"code":2980,"language":22,"meta":28,"style":28},"function createFunctions() {\n    const fns = [];\n    for (let i = 0; i \u003C 3; i++) {\n        fns.push(() => i);\n    }\n    return fns;\n}\nconst [a, b, c] = createFunctions();\nconsole.log(a(), b(), c());\n",[30,2982,2983,2992,3004,3031,3045,3049,3056,3060,3085],{"__ignoreMap":28},[33,2984,2985,2987,2990],{"class":35,"line":36},[33,2986,66],{"class":65},[33,2988,2989],{"class":69}," createFunctions",[33,2991,74],{"class":73},[33,2993,2994,2996,2999,3001],{"class":35,"line":43},[33,2995,80],{"class":65},[33,2997,2998],{"class":83}," fns",[33,3000,87],{"class":65},[33,3002,3003],{"class":73}," [];\n",[33,3005,3006,3009,3011,3013,3015,3017,3019,3021,3023,3025,3027,3029],{"class":35,"line":49},[33,3007,3008],{"class":65},"    for",[33,3010,417],{"class":73},[33,3012,498],{"class":65},[33,3014,423],{"class":73},[33,3016,426],{"class":65},[33,3018,429],{"class":83},[33,3020,432],{"class":73},[33,3022,435],{"class":65},[33,3024,286],{"class":83},[33,3026,440],{"class":73},[33,3028,443],{"class":65},[33,3030,446],{"class":73},[33,3032,3033,3036,3038,3040,3042],{"class":35,"line":55},[33,3034,3035],{"class":73},"        fns.",[33,3037,2836],{"class":69},[33,3039,454],{"class":73},[33,3041,457],{"class":65},[33,3043,3044],{"class":73}," i);\n",[33,3046,3047],{"class":35,"line":62},[33,3048,140],{"class":73},[33,3050,3051,3053],{"class":35,"line":77},[33,3052,151],{"class":65},[33,3054,3055],{"class":73}," fns;\n",[33,3057,3058],{"class":35,"line":100},[33,3059,163],{"class":73},[33,3061,3062,3064,3066,3068,3070,3072,3074,3076,3079,3081,3083],{"class":35,"line":105},[33,3063,174],{"class":65},[33,3065,2366],{"class":73},[33,3067,2240],{"class":83},[33,3069,1809],{"class":73},[33,3071,2245],{"class":83},[33,3073,1809],{"class":73},[33,3075,2250],{"class":83},[33,3077,3078],{"class":73},"] ",[33,3080,426],{"class":65},[33,3082,2989],{"class":69},[33,3084,329],{"class":73},[33,3086,3087,3089,3091,3093,3095,3098,3100,3102,3104],{"class":35,"line":116},[33,3088,872],{"class":73},[33,3090,128],{"class":69},[33,3092,588],{"class":73},[33,3094,2240],{"class":69},[33,3096,3097],{"class":73},"(), ",[33,3099,2245],{"class":69},[33,3101,3097],{"class":73},[33,3103,2250],{"class":69},[33,3105,3106],{"class":73},"());\n",[3108,3109,3110,3114,3122,3139,3168,3183],"details",{},[3111,3112,3113],"summary",{},"Answer",[24,3115,3120],{"className":3116,"code":3118,"language":3119},[3117],"language-text","0 1 2\n","text",[30,3121,3118],{"__ignoreMap":28},[2973,3123,3124,3125,3127,3128,3132,3133,3136,3137,1185],{},"With ",[30,3126,498],{}," in the loop, each iteration creates a ",[3129,3130,3131],"strong",{},"new binding"," for ",[30,3134,3135],{},"i",". Each closure captures its own ",[30,3138,3135],{},[3140,3141,3142,3152,3160],"ul",{},[3143,3144,3145,3147,3148,3151],"li",{},[30,3146,2240],{}," captures ",[30,3149,3150],{},"i = 0"," (first iteration's binding)",[3143,3153,3154,3147,3156,3159],{},[30,3155,2245],{},[30,3157,3158],{},"i = 1"," (second iteration's binding)",[3143,3161,3162,3147,3164,3167],{},[30,3163,2250],{},[30,3165,3166],{},"i = 2"," (third iteration's binding)",[2973,3169,3170,3171,3173,3174,3176,3177,3179,3180,1063],{},"If ",[30,3172,420],{}," were used instead of ",[30,3175,498],{},", all three closures would share the same ",[30,3178,3135],{}," (which is 3 by the time the loop ends), and the output would be ",[30,3181,3182],{},"3 3 3",[2973,3184,3185,3188,3189,3191,3192,3194,3195,3197,3198,3200,3201,3203],{},[3129,3186,3187],{},"The lesson",": ",[30,3190,498],{}," in a ",[30,3193,414],{}," loop creates a per-iteration binding. Each closure captures a different ",[30,3196,3135],{},". ",[30,3199,420],{}," creates a single function-scoped binding — all closures share the same ",[30,3202,3135],{}," (which ends at 3).",[3205,3206,3207],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}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":28,"searchDepth":43,"depth":43,"links":3209},[3210,3211,3212,3213,3214,3215,3216,3217],{"id":17,"depth":43,"text":18},{"id":391,"depth":43,"text":392},{"id":691,"depth":43,"text":692},{"id":1235,"depth":43,"text":1236},{"id":1613,"depth":43,"text":1614},{"id":2093,"depth":43,"text":2094},{"id":2565,"depth":43,"text":2566},{"id":2970,"depth":43,"text":2971},"Deep-dive into JavaScript closures: the lexical environment and scope chain, variable capture semantics, the classic loop closure bug, closures for encapsulation and memoization, and closure-induced memory leaks. Code-first reference for senior engineers.","md",{},"\u002Fjs\u002F12-closures-and-lexical-scope",{"title":5,"description":3218},"js\u002F12-closures-and-lexical-scope","-TT4LEs9rYgDZYlrjPIgDw4KJ0E1Yd84bpxQ_8pwY5c",1789924649230]