[{"data":1,"prerenderedAt":1320},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjavascript-closures-explained-patterns":3},{"id":4,"title":5,"body":6,"description":1304,"difficulty":1305,"extension":1306,"framework":1307,"frameworkSlug":1308,"meta":1309,"navigation":1310,"order":52,"path":1311,"qaPath":1312,"seo":1313,"stem":1314,"subtopic":1315,"topic":1316,"topicSlug":1317,"updated":1318,"__hash__":1319},"blog\u002Fblog\u002Fjavascript-closures-explained-patterns.md","JavaScript Closures Explained — From Basics to Advanced Patterns",{"type":7,"value":8,"toc":1291},"minimark",[9,14,23,27,39,157,173,177,184,253,264,321,325,342,443,449,505,508,512,515,668,675,679,682,751,761,827,831,834,927,938,1029,1044,1051,1068,1174,1184,1188,1191,1251,1261,1265,1287],[10,11,13],"h2",{"id":12},"javascript-closures-explained","JavaScript closures, explained",[15,16,17,18,22],"p",{},"Closures are one of the most powerful and most misunderstood features of JavaScript.\nThey're behind data privacy, function factories, currying, memoization, debounce and\nthrottle, and the famous ",[19,20,21],"code",{},"var","-in-a-loop bug. Once the underlying idea clicks, a whole\nclass of \"why does this happen?\" questions answers itself. This guide goes from the\ncore definition to the practical patterns built on it.",[10,24,26],{"id":25},"what-a-closure-is","What a closure is",[15,28,29,30,34,35,38],{},"A closure is a function bundled together with a reference to the ",[31,32,33],"strong",{},"lexical scope"," in\nwhich it was created. Because of that bundle, the inner function keeps access to its\nouter variables ",[31,36,37],{},"even after the outer function has returned",".",[40,41,46],"pre",{"className":42,"code":43,"language":44,"meta":45,"style":45},"language-js shiki shiki-themes github-light github-dark","function counter() {\n  let count = 0          \u002F\u002F lives in counter's scope\n  return () => ++count   \u002F\u002F this arrow closes over count\n}\nconst next = counter()   \u002F\u002F counter() has returned...\nnext() \u002F\u002F 1              \u002F\u002F ...yet count is still alive and remembered\nnext() \u002F\u002F 2\n","js","",[19,47,48,65,85,106,112,132,147],{"__ignoreMap":45},[49,50,53,57,61],"span",{"class":51,"line":52},"line",1,[49,54,56],{"class":55},"szBVR","function",[49,58,60],{"class":59},"sScJk"," counter",[49,62,64],{"class":63},"sVt8B","() {\n",[49,66,68,71,74,77,81],{"class":51,"line":67},2,[49,69,70],{"class":55},"  let",[49,72,73],{"class":63}," count ",[49,75,76],{"class":55},"=",[49,78,80],{"class":79},"sj4cs"," 0",[49,82,84],{"class":83},"sJ8bj","          \u002F\u002F lives in counter's scope\n",[49,86,88,91,94,97,100,103],{"class":51,"line":87},3,[49,89,90],{"class":55},"  return",[49,92,93],{"class":63}," () ",[49,95,96],{"class":55},"=>",[49,98,99],{"class":55}," ++",[49,101,102],{"class":63},"count   ",[49,104,105],{"class":83},"\u002F\u002F this arrow closes over count\n",[49,107,109],{"class":51,"line":108},4,[49,110,111],{"class":63},"}\n",[49,113,115,118,121,124,126,129],{"class":51,"line":114},5,[49,116,117],{"class":55},"const",[49,119,120],{"class":79}," next",[49,122,123],{"class":55}," =",[49,125,60],{"class":59},[49,127,128],{"class":63},"()   ",[49,130,131],{"class":83},"\u002F\u002F counter() has returned...\n",[49,133,135,138,141,144],{"class":51,"line":134},6,[49,136,137],{"class":59},"next",[49,139,140],{"class":63},"() ",[49,142,143],{"class":83},"\u002F\u002F 1",[49,145,146],{"class":83},"              \u002F\u002F ...yet count is still alive and remembered\n",[49,148,150,152,154],{"class":51,"line":149},7,[49,151,137],{"class":59},[49,153,140],{"class":63},[49,155,156],{"class":83},"\u002F\u002F 2\n",[15,158,159,160,163,164,167,168,172],{},"The crucial detail: a closure captures the ",[31,161,162],{},"variable (binding), not a snapshot of its\nvalue",". So ",[19,165,166],{},"count"," is genuinely shared and mutable across calls. Technically every\nfunction is a closure; it only ",[169,170,171],"em",{},"matters"," when the function outlives the scope it\ncaptured.",[10,174,176],{"id":175},"lexical-scope-and-the-scope-chain","Lexical scope and the scope chain",[15,178,179,180,183],{},"\"Lexical\" means scope is determined by ",[31,181,182],{},"where code is written",", not where it's\ncalled. When you reference a variable, the engine searches the current scope, then each\nenclosing scope outward, until the global scope — the scope chain.",[40,185,187],{"className":42,"code":186,"language":44,"meta":45,"style":45},"function outer() {\n  const x = 10\n  function inner() { return x } \u002F\u002F resolves x by lexical position\n  return inner\n}\nouter()() \u002F\u002F 10 — inner remembers outer's scope wherever it runs\n",[19,188,189,198,211,231,238,242],{"__ignoreMap":45},[49,190,191,193,196],{"class":51,"line":52},[49,192,56],{"class":55},[49,194,195],{"class":59}," outer",[49,197,64],{"class":63},[49,199,200,203,206,208],{"class":51,"line":67},[49,201,202],{"class":55},"  const",[49,204,205],{"class":79}," x",[49,207,123],{"class":55},[49,209,210],{"class":79}," 10\n",[49,212,213,216,219,222,225,228],{"class":51,"line":87},[49,214,215],{"class":55},"  function",[49,217,218],{"class":59}," inner",[49,220,221],{"class":63},"() { ",[49,223,224],{"class":55},"return",[49,226,227],{"class":63}," x } ",[49,229,230],{"class":83},"\u002F\u002F resolves x by lexical position\n",[49,232,233,235],{"class":51,"line":108},[49,234,90],{"class":55},[49,236,237],{"class":63}," inner\n",[49,239,240],{"class":51,"line":114},[49,241,111],{"class":63},[49,243,244,247,250],{"class":51,"line":134},[49,245,246],{"class":59},"outer",[49,248,249],{"class":63},"()() ",[49,251,252],{"class":83},"\u002F\u002F 10 — inner remembers outer's scope wherever it runs\n",[15,254,255,256,259,260,263],{},"Because scope is fixed at definition time, you can reason about what any closure can\naccess just by reading the nesting. And because each ",[169,257,258],{},"call"," to an outer function\ncreates a fresh scope, each returned closure captures ",[31,261,262],{},"independent"," state:",[40,265,267],{"className":42,"code":266,"language":44,"meta":45,"style":45},"const a = counter()\nconst b = counter()\na(); a() \u002F\u002F 2\nb()      \u002F\u002F 1 — separate closure, separate count\n",[19,268,269,283,296,310],{"__ignoreMap":45},[49,270,271,273,276,278,280],{"class":51,"line":52},[49,272,117],{"class":55},[49,274,275],{"class":79}," a",[49,277,123],{"class":55},[49,279,60],{"class":59},[49,281,282],{"class":63},"()\n",[49,284,285,287,290,292,294],{"class":51,"line":67},[49,286,117],{"class":55},[49,288,289],{"class":79}," b",[49,291,123],{"class":55},[49,293,60],{"class":59},[49,295,282],{"class":63},[49,297,298,301,304,306,308],{"class":51,"line":87},[49,299,300],{"class":59},"a",[49,302,303],{"class":63},"(); ",[49,305,300],{"class":59},[49,307,140],{"class":63},[49,309,156],{"class":83},[49,311,312,315,318],{"class":51,"line":108},[49,313,314],{"class":59},"b",[49,316,317],{"class":63},"()      ",[49,319,320],{"class":83},"\u002F\u002F 1 — separate closure, separate count\n",[10,322,324],{"id":323},"value-vs-binding-the-loop-bug","Value vs binding: the loop bug",[15,326,327,328,330,331,334,335,338,339,38],{},"The most famous closure question. With ",[19,329,21],{}," (function-scoped), every iteration shares\n",[31,332,333],{},"one"," binding, so deferred callbacks all read its final value. ",[19,336,337],{},"let"," (block-scoped)\ncreates a ",[31,340,341],{},"fresh binding per iteration",[40,343,345],{"className":42,"code":344,"language":44,"meta":45,"style":45},"for (var i = 0; i \u003C 3; i++) setTimeout(() => console.log(i)) \u002F\u002F 3 3 3\nfor (let i = 0; i \u003C 3; i++) setTimeout(() => console.log(i)) \u002F\u002F 0 1 2\n",[19,346,347,402],{"__ignoreMap":45},[49,348,349,352,355,357,360,362,364,367,370,373,376,379,382,385,388,390,393,396,399],{"class":51,"line":52},[49,350,351],{"class":55},"for",[49,353,354],{"class":63}," (",[49,356,21],{"class":55},[49,358,359],{"class":63}," i ",[49,361,76],{"class":55},[49,363,80],{"class":79},[49,365,366],{"class":63},"; i ",[49,368,369],{"class":55},"\u003C",[49,371,372],{"class":79}," 3",[49,374,375],{"class":63},"; i",[49,377,378],{"class":55},"++",[49,380,381],{"class":63},") ",[49,383,384],{"class":59},"setTimeout",[49,386,387],{"class":63},"(() ",[49,389,96],{"class":55},[49,391,392],{"class":63}," console.",[49,394,395],{"class":59},"log",[49,397,398],{"class":63},"(i)) ",[49,400,401],{"class":83},"\u002F\u002F 3 3 3\n",[49,403,404,406,408,410,412,414,416,418,420,422,424,426,428,430,432,434,436,438,440],{"class":51,"line":67},[49,405,351],{"class":55},[49,407,354],{"class":63},[49,409,337],{"class":55},[49,411,359],{"class":63},[49,413,76],{"class":55},[49,415,80],{"class":79},[49,417,366],{"class":63},[49,419,369],{"class":55},[49,421,372],{"class":79},[49,423,375],{"class":63},[49,425,378],{"class":55},[49,427,381],{"class":63},[49,429,384],{"class":59},[49,431,387],{"class":63},[49,433,96],{"class":55},[49,435,392],{"class":63},[49,437,395],{"class":59},[49,439,398],{"class":63},[49,441,442],{"class":83},"\u002F\u002F 0 1 2\n",[15,444,445,446,448],{},"Before ",[19,447,337],{},", the fix was an IIFE that manufactured a new scope per iteration, capturing\nthe value as a parameter:",[40,450,452],{"className":42,"code":451,"language":44,"meta":45,"style":45},"for (var i = 0; i \u003C 3; i++) ((j) => setTimeout(() => console.log(j)))(i) \u002F\u002F 0 1 2\n",[19,453,454],{"__ignoreMap":45},[49,455,456,458,460,462,464,466,468,470,472,474,476,478,481,485,487,489,492,494,496,498,500,503],{"class":51,"line":52},[49,457,351],{"class":55},[49,459,354],{"class":63},[49,461,21],{"class":55},[49,463,359],{"class":63},[49,465,76],{"class":55},[49,467,80],{"class":79},[49,469,366],{"class":63},[49,471,369],{"class":55},[49,473,372],{"class":79},[49,475,375],{"class":63},[49,477,378],{"class":55},[49,479,480],{"class":63},") ((",[49,482,484],{"class":483},"s4XuR","j",[49,486,381],{"class":63},[49,488,96],{"class":55},[49,490,491],{"class":59}," setTimeout",[49,493,387],{"class":63},[49,495,96],{"class":55},[49,497,392],{"class":63},[49,499,395],{"class":59},[49,501,502],{"class":63},"(j)))(i) ",[49,504,442],{"class":83},[15,506,507],{},"This is the same mechanism behind stale closures in callbacks and React renders: a\nfunction captures the values from the render\u002Fiteration it was created in, and never sees\nlater changes unless it's recreated.",[10,509,511],{"id":510},"data-privacy-and-the-module-pattern","Data privacy and the module pattern",[15,513,514],{},"Variables in an enclosing scope are unreachable from outside — there's no reference to\nthem except through the functions you expose. That's true encapsulation.",[40,516,518],{"className":42,"code":517,"language":44,"meta":45,"style":45},"function account() {\n  let balance = 0 \u002F\u002F private\n  return {\n    deposit: n => (balance += n),\n    withdraw: n => (balance -= n),\n    get: () => balance,\n  }\n}\nconst a = account()\na.deposit(100)\na.get()     \u002F\u002F 100\na.balance   \u002F\u002F undefined — inaccessible\n",[19,519,520,529,543,550,573,591,604,609,614,627,645,659],{"__ignoreMap":45},[49,521,522,524,527],{"class":51,"line":52},[49,523,56],{"class":55},[49,525,526],{"class":59}," account",[49,528,64],{"class":63},[49,530,531,533,536,538,540],{"class":51,"line":67},[49,532,70],{"class":55},[49,534,535],{"class":63}," balance ",[49,537,76],{"class":55},[49,539,80],{"class":79},[49,541,542],{"class":83}," \u002F\u002F private\n",[49,544,545,547],{"class":51,"line":87},[49,546,90],{"class":55},[49,548,549],{"class":63}," {\n",[49,551,552,555,558,561,564,567,570],{"class":51,"line":108},[49,553,554],{"class":59},"    deposit",[49,556,557],{"class":63},": ",[49,559,560],{"class":483},"n",[49,562,563],{"class":55}," =>",[49,565,566],{"class":63}," (balance ",[49,568,569],{"class":55},"+=",[49,571,572],{"class":63}," n),\n",[49,574,575,578,580,582,584,586,589],{"class":51,"line":114},[49,576,577],{"class":59},"    withdraw",[49,579,557],{"class":63},[49,581,560],{"class":483},[49,583,563],{"class":55},[49,585,566],{"class":63},[49,587,588],{"class":55},"-=",[49,590,572],{"class":63},[49,592,593,596,599,601],{"class":51,"line":134},[49,594,595],{"class":59},"    get",[49,597,598],{"class":63},": () ",[49,600,96],{"class":55},[49,602,603],{"class":63}," balance,\n",[49,605,606],{"class":51,"line":149},[49,607,608],{"class":63},"  }\n",[49,610,612],{"class":51,"line":611},8,[49,613,111],{"class":63},[49,615,617,619,621,623,625],{"class":51,"line":616},9,[49,618,117],{"class":55},[49,620,275],{"class":79},[49,622,123],{"class":55},[49,624,526],{"class":59},[49,626,282],{"class":63},[49,628,630,633,636,639,642],{"class":51,"line":629},10,[49,631,632],{"class":63},"a.",[49,634,635],{"class":59},"deposit",[49,637,638],{"class":63},"(",[49,640,641],{"class":79},"100",[49,643,644],{"class":63},")\n",[49,646,648,650,653,656],{"class":51,"line":647},11,[49,649,632],{"class":63},[49,651,652],{"class":59},"get",[49,654,655],{"class":63},"()     ",[49,657,658],{"class":83},"\u002F\u002F 100\n",[49,660,662,665],{"class":51,"line":661},12,[49,663,664],{"class":63},"a.balance   ",[49,666,667],{"class":83},"\u002F\u002F undefined — inaccessible\n",[15,669,670,671,674],{},"The ",[31,672,673],{},"module pattern"," (often an IIFE) uses this to keep private state and expose only a\npublic API — the standard way to organize code before ES modules, and still useful for\nencapsulated singletons.",[10,676,678],{"id":677},"function-factories-currying-partial-application","Function factories, currying, partial application",[15,680,681],{},"A function factory returns customized functions, each closing over its creation\narguments:",[40,683,685],{"className":42,"code":684,"language":44,"meta":45,"style":45},"const makeMultiplier = factor => n => n * factor\nconst double = makeMultiplier(2) \u002F\u002F remembers factor = 2\ndouble(5) \u002F\u002F 10\n",[19,686,687,715,736],{"__ignoreMap":45},[49,688,689,691,694,696,699,701,704,706,709,712],{"class":51,"line":52},[49,690,117],{"class":55},[49,692,693],{"class":59}," makeMultiplier",[49,695,123],{"class":55},[49,697,698],{"class":483}," factor",[49,700,563],{"class":55},[49,702,703],{"class":483}," n",[49,705,563],{"class":55},[49,707,708],{"class":63}," n ",[49,710,711],{"class":55},"*",[49,713,714],{"class":63}," factor\n",[49,716,717,719,722,724,726,728,731,733],{"class":51,"line":67},[49,718,117],{"class":55},[49,720,721],{"class":79}," double",[49,723,123],{"class":55},[49,725,693],{"class":59},[49,727,638],{"class":63},[49,729,730],{"class":79},"2",[49,732,381],{"class":63},[49,734,735],{"class":83},"\u002F\u002F remembers factor = 2\n",[49,737,738,741,743,746,748],{"class":51,"line":87},[49,739,740],{"class":59},"double",[49,742,638],{"class":63},[49,744,745],{"class":79},"5",[49,747,381],{"class":63},[49,749,750],{"class":83},"\u002F\u002F 10\n",[15,752,753,756,757,760],{},[31,754,755],{},"Currying"," transforms a multi-argument function into a chain of single-argument ones,\neach closing over earlier args; ",[31,758,759],{},"partial application"," fixes some arguments now and\nreturns a function for the rest. Both rely entirely on closures remembering the bound\nvalues.",[40,762,764],{"className":42,"code":763,"language":44,"meta":45,"style":45},"const add = a => b => c => a + b + c\nadd(1)(2)(3) \u002F\u002F 6\n",[19,765,766,802],{"__ignoreMap":45},[49,767,768,770,773,775,777,779,781,783,786,788,791,794,797,799],{"class":51,"line":52},[49,769,117],{"class":55},[49,771,772],{"class":59}," add",[49,774,123],{"class":55},[49,776,275],{"class":483},[49,778,563],{"class":55},[49,780,289],{"class":483},[49,782,563],{"class":55},[49,784,785],{"class":483}," c",[49,787,563],{"class":55},[49,789,790],{"class":63}," a ",[49,792,793],{"class":55},"+",[49,795,796],{"class":63}," b ",[49,798,793],{"class":55},[49,800,801],{"class":63}," c\n",[49,803,804,807,809,812,815,817,819,822,824],{"class":51,"line":67},[49,805,806],{"class":59},"add",[49,808,638],{"class":63},[49,810,811],{"class":79},"1",[49,813,814],{"class":63},")(",[49,816,730],{"class":79},[49,818,814],{"class":63},[49,820,821],{"class":79},"3",[49,823,381],{"class":63},[49,825,826],{"class":83},"\u002F\u002F 6\n",[10,828,830],{"id":829},"stateful-utilities-memoize-once-debounce-throttle","Stateful utilities: memoize, once, debounce, throttle",[15,832,833],{},"Closures shine when you need persistent private state between calls. A memoizer keeps a\nprivate cache:",[40,835,837],{"className":42,"code":836,"language":44,"meta":45,"style":45},"function memoize(fn) {\n  const cache = new Map()       \u002F\u002F private, persists across calls\n  return arg => cache.has(arg) ? cache.get(arg) : cache.set(arg, fn(arg)).get(arg)\n}\n",[19,838,839,854,875,923],{"__ignoreMap":45},[49,840,841,843,846,848,851],{"class":51,"line":52},[49,842,56],{"class":55},[49,844,845],{"class":59}," memoize",[49,847,638],{"class":63},[49,849,850],{"class":483},"fn",[49,852,853],{"class":63},") {\n",[49,855,856,858,861,863,866,869,872],{"class":51,"line":67},[49,857,202],{"class":55},[49,859,860],{"class":79}," cache",[49,862,123],{"class":55},[49,864,865],{"class":55}," new",[49,867,868],{"class":59}," Map",[49,870,871],{"class":63},"()       ",[49,873,874],{"class":83},"\u002F\u002F private, persists across calls\n",[49,876,877,879,882,884,887,890,893,896,898,900,902,905,907,910,913,915,918,920],{"class":51,"line":87},[49,878,90],{"class":55},[49,880,881],{"class":483}," arg",[49,883,563],{"class":55},[49,885,886],{"class":63}," cache.",[49,888,889],{"class":59},"has",[49,891,892],{"class":63},"(arg) ",[49,894,895],{"class":55},"?",[49,897,886],{"class":63},[49,899,652],{"class":59},[49,901,892],{"class":63},[49,903,904],{"class":55},":",[49,906,886],{"class":63},[49,908,909],{"class":59},"set",[49,911,912],{"class":63},"(arg, ",[49,914,850],{"class":59},[49,916,917],{"class":63},"(arg)).",[49,919,652],{"class":59},[49,921,922],{"class":63},"(arg)\n",[49,924,925],{"class":51,"line":108},[49,926,111],{"class":63},[15,928,929,930,933,934,937],{},"Debounce and throttle hold a timer\u002Ftimestamp in the closure. ",[31,931,932],{},"Debounce"," runs after\nactivity stops; ",[31,935,936],{},"throttle"," runs at most once per interval:",[40,939,941],{"className":42,"code":940,"language":44,"meta":45,"style":45},"function debounce(fn, delay) {\n  let timer                      \u002F\u002F remembered between calls\n  return (...args) => {\n    clearTimeout(timer)\n    timer = setTimeout(() => fn(...args), delay)\n  }\n}\n",[19,942,943,962,972,990,998,1021,1025],{"__ignoreMap":45},[49,944,945,947,950,952,954,957,960],{"class":51,"line":52},[49,946,56],{"class":55},[49,948,949],{"class":59}," debounce",[49,951,638],{"class":63},[49,953,850],{"class":483},[49,955,956],{"class":63},", ",[49,958,959],{"class":483},"delay",[49,961,853],{"class":63},[49,963,964,966,969],{"class":51,"line":67},[49,965,70],{"class":55},[49,967,968],{"class":63}," timer                      ",[49,970,971],{"class":83},"\u002F\u002F remembered between calls\n",[49,973,974,976,978,981,984,986,988],{"class":51,"line":87},[49,975,90],{"class":55},[49,977,354],{"class":63},[49,979,980],{"class":55},"...",[49,982,983],{"class":483},"args",[49,985,381],{"class":63},[49,987,96],{"class":55},[49,989,549],{"class":63},[49,991,992,995],{"class":51,"line":108},[49,993,994],{"class":59},"    clearTimeout",[49,996,997],{"class":63},"(timer)\n",[49,999,1000,1003,1005,1007,1009,1011,1014,1016,1018],{"class":51,"line":114},[49,1001,1002],{"class":63},"    timer ",[49,1004,76],{"class":55},[49,1006,491],{"class":59},[49,1008,387],{"class":63},[49,1010,96],{"class":55},[49,1012,1013],{"class":59}," fn",[49,1015,638],{"class":63},[49,1017,980],{"class":55},[49,1019,1020],{"class":63},"args), delay)\n",[49,1022,1023],{"class":51,"line":134},[49,1024,608],{"class":63},[49,1026,1027],{"class":51,"line":149},[49,1028,111],{"class":63},[15,1030,1031,1032,1035,1036,1039,1040,1043],{},"A ",[19,1033,1034],{},"once"," wrapper remembers whether it has run; an encapsulated counter exposes\n",[19,1037,1038],{},"increment","\u002F",[19,1041,1042],{},"reset"," methods over a private variable. All the same idea: state hidden in\na closure.",[10,1045,1047,1048],{"id":1046},"closures-and-this","Closures and ",[19,1049,1050],{},"this",[15,1052,1053,1054,1057,1058,1060,1061,1064,1065,1067],{},"A subtle point: closures capture ",[31,1055,1056],{},"variables",", but ",[19,1059,1050],{}," is ",[169,1062,1063],{},"not"," an ordinary\nvariable for regular functions — it's set by how the function is called. Arrow\nfunctions, however, capture ",[19,1066,1050],{}," lexically, like a closed-over variable.",[40,1069,1071],{"className":42,"code":1070,"language":44,"meta":45,"style":45},"const obj = {\n  name: 'Ada',\n  regular() { return function () { return this.name } }, \u002F\u002F this lost\n  arrow()   { return () => this.name },                  \u002F\u002F this captured\n}\nobj.regular()() \u002F\u002F undefined\nobj.arrow()()   \u002F\u002F 'Ada'\n",[19,1072,1073,1084,1096,1122,1144,1148,1161],{"__ignoreMap":45},[49,1074,1075,1077,1080,1082],{"class":51,"line":52},[49,1076,117],{"class":55},[49,1078,1079],{"class":79}," obj",[49,1081,123],{"class":55},[49,1083,549],{"class":63},[49,1085,1086,1089,1093],{"class":51,"line":67},[49,1087,1088],{"class":63},"  name: ",[49,1090,1092],{"class":1091},"sZZnC","'Ada'",[49,1094,1095],{"class":63},",\n",[49,1097,1098,1101,1103,1105,1108,1111,1113,1116,1119],{"class":51,"line":87},[49,1099,1100],{"class":59},"  regular",[49,1102,221],{"class":63},[49,1104,224],{"class":55},[49,1106,1107],{"class":55}," function",[49,1109,1110],{"class":63}," () { ",[49,1112,224],{"class":55},[49,1114,1115],{"class":79}," this",[49,1117,1118],{"class":63},".name } }, ",[49,1120,1121],{"class":83},"\u002F\u002F this lost\n",[49,1123,1124,1127,1130,1132,1134,1136,1138,1141],{"class":51,"line":108},[49,1125,1126],{"class":59},"  arrow",[49,1128,1129],{"class":63},"()   { ",[49,1131,224],{"class":55},[49,1133,93],{"class":63},[49,1135,96],{"class":55},[49,1137,1115],{"class":79},[49,1139,1140],{"class":63},".name },                  ",[49,1142,1143],{"class":83},"\u002F\u002F this captured\n",[49,1145,1146],{"class":51,"line":114},[49,1147,111],{"class":63},[49,1149,1150,1153,1156,1158],{"class":51,"line":134},[49,1151,1152],{"class":63},"obj.",[49,1154,1155],{"class":59},"regular",[49,1157,249],{"class":63},[49,1159,1160],{"class":83},"\u002F\u002F undefined\n",[49,1162,1163,1165,1168,1171],{"class":51,"line":149},[49,1164,1152],{"class":63},[49,1166,1167],{"class":59},"arrow",[49,1169,1170],{"class":63},"()()   ",[49,1172,1173],{"class":83},"\u002F\u002F 'Ada'\n",[15,1175,1176,1177,1180,1181,1183],{},"The pre-arrow workaround ",[19,1178,1179],{},"const self = this"," captured ",[19,1182,1050],{}," as a real closed-over\nvariable.",[10,1185,1187],{"id":1186},"memory-considerations","Memory considerations",[15,1189,1190],{},"A closure keeps its captured scope alive as long as the closure is reachable. That's\nusually fine, but holding a closure that references large objects or DOM nodes — for\nexample an event listener you never remove — retains that memory indefinitely.",[40,1192,1194],{"className":42,"code":1193,"language":44,"meta":45,"style":45},"const big = new Array(1e6)\nel.addEventListener('click', () => console.log(big.length)) \u002F\u002F pins `big`\n",[19,1195,1196,1217],{"__ignoreMap":45},[49,1197,1198,1200,1203,1205,1207,1210,1212,1215],{"class":51,"line":52},[49,1199,117],{"class":55},[49,1201,1202],{"class":79}," big",[49,1204,123],{"class":55},[49,1206,865],{"class":55},[49,1208,1209],{"class":59}," Array",[49,1211,638],{"class":63},[49,1213,1214],{"class":79},"1e6",[49,1216,644],{"class":63},[49,1218,1219,1222,1225,1227,1230,1233,1235,1237,1239,1242,1245,1248],{"class":51,"line":67},[49,1220,1221],{"class":63},"el.",[49,1223,1224],{"class":59},"addEventListener",[49,1226,638],{"class":63},[49,1228,1229],{"class":1091},"'click'",[49,1231,1232],{"class":63},", () ",[49,1234,96],{"class":55},[49,1236,392],{"class":63},[49,1238,395],{"class":59},[49,1240,1241],{"class":63},"(big.",[49,1243,1244],{"class":79},"length",[49,1246,1247],{"class":63},")) ",[49,1249,1250],{"class":83},"\u002F\u002F pins `big`\n",[15,1252,1253,1254,1039,1257,1260],{},"Release references when done (remove listeners, null large objects), and consider\n",[19,1255,1256],{},"WeakMap",[19,1258,1259],{},"WeakRef"," for caches so entries can be collected.",[10,1262,1264],{"id":1263},"recap","Recap",[15,1266,1267,1268,1271,1272,1275,1276,956,1279,1282,1283,1286],{},"A closure is a function plus the lexical scope it remembers, captured by ",[31,1269,1270],{},"binding, not\nvalue"," — which explains the loop bug, stale closures, and shared vs independent state.\nThat single mechanism powers ",[31,1273,1274],{},"data privacy"," (module pattern, private variables),\n",[31,1277,1278],{},"function factories",[31,1280,1281],{},"currying and partial application",", and ",[31,1284,1285],{},"stateful utilities","\nlike memoize, debounce, and throttle. Understand binding capture and lexical scope, mind\nthe memory implications, and closures become a tool you reach for deliberately rather\nthan a source of mystery.",[1288,1289,1290],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":45,"searchDepth":67,"depth":67,"links":1292},[1293,1294,1295,1296,1297,1298,1299,1300,1302,1303],{"id":12,"depth":67,"text":13},{"id":25,"depth":67,"text":26},{"id":175,"depth":67,"text":176},{"id":323,"depth":67,"text":324},{"id":510,"depth":67,"text":511},{"id":677,"depth":67,"text":678},{"id":829,"depth":67,"text":830},{"id":1046,"depth":67,"text":1301},"Closures and this",{"id":1186,"depth":67,"text":1187},{"id":1263,"depth":67,"text":1264},"JavaScript closure interview questions and answers — what closures are, how they capture variables, practical uses and common pitfalls.","medium","md","JavaScript","javascript",{},true,"\u002Fblog\u002Fjavascript-closures-explained-patterns","\u002Fjavascript\u002Ffunctions\u002Fclosures",{"title":5,"description":1304},"blog\u002Fjavascript-closures-explained-patterns","Closures","Functions","functions","2026-06-17","xj8Q_fTgFl-WgOSRtdGE8cuiyyKhAUzsgCSxTeXGfpg",1781808673080]