[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-weekly-javascript-closures":44,"search-suggestions":429,"related-weekly-javascript-closures":467,"code:js:true:1h2y0m4":591,"code:js:true:42dc47":592,"code:js:true:t4tv4k":593,"code:js:true:1fhvn98":594,"code:js:true:1okgb7x":595,"code:js:true:wmpbds":596,"code:js:true:1va043q":597,"code:js:true:2zgam3":598,"code:js:true:4g27l8":599,"comments-019fe661-0c1b-7408-8419-531dae95bdf1":600},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":53,"likeCount":19,"commentCount":19,"author":54,"vertical":59,"topic":60,"tags":63,"_count":75,"playground":77,"body":79,"bodyMd":390,"seo":391,"translationGroupId":393,"series":394,"verticalId":5,"thread":417,"assessments":419,"translations":425,"quiz":427},"019fe661-0c1b-7408-8419-531dae95bdf1","weekly-javascript-closures","JavaScript Closures: The Complete Guide (with Cheat Sheet)",null,"Learn how JavaScript closures really work — lexical scope, private state, memoization, and the classic loop bug — with worked examples and a cheat sheet.","\u002Fmedia\u002Fcovers\u002Fweekly-javascript-closures.png",14,"2026-08-25T08:53:48.904Z",70,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},"javascript","JavaScript",[64,66,69,72],{"slug":61,"name":65,"color":48},"Javascript",{"slug":67,"name":68,"color":48},"webdev","Webdev",{"slug":70,"name":71,"color":48},"tutorial","Tutorial",{"slug":73,"name":74,"color":48},"programming","Programming",{"assessments":76},1,{"slug":46,"title":78},"JavaScript closures — interactive playground",{"blocks":80,"version":76},[81,85,90,93,102,105,108,122,125,128,134,137,140,143,146,149,153,156,159,162,166,170,173,176,179,183,186,189,192,196,199,202,205,208,211,214,217,220,223,227,230,234,237,241,244,247,255,258,261,264,267,270,273,276,279,282,285,288,291,294,298,301,304,307,310,342,346,349,352,355,358,361,364,372,375,378,381,384],{"id":82,"html":83,"type":84},"b1","\u003Cp>Open five browser tabs, click a button in each, and every single one logs \u003Ccode>5\u003C\u002Fcode>. Not \u003Ccode>1\u003C\u002Fcode>, \u003Ccode>2\u003C\u002Fcode>, \u003Ccode>3\u003C\u002Fcode>, \u003Ccode>4\u003C\u002Fcode>, \u003Ccode>5\u003C\u002Fcode> — five identical \u003Ccode>5\u003C\u002Fcode>s, as if the loop that created the buttons never ran at all. This is one of the oldest, most reliable JavaScript bugs there is, and it isn&#39;t a bug in JavaScript — it&#39;s a closure doing \u003Cem>exactly\u003C\u002Fem> what it&#39;s supposed to do, with a variable you didn&#39;t realize you were sharing.\u003C\u002Fp>","paragraph",{"id":86,"html":87,"text":88,"type":89,"level":31},"b2","What you&#39;ll learn","What you'll learn","heading",{"id":91,"html":92,"type":84},"b3","\u003Cp>By the end of this guide you&#39;ll be able to:\u003C\u002Fp>",{"id":94,"type":95,"items":96,"ordered":18},"b4","list",[97,98,99,100,101],"Explain what a closure actually is — a function bundled with a live reference to its scope, not a copy of it","Use closures to build private state without a class","Write a memoization cache and a &quot;run once&quot; guard using nothing but closures","Diagnose the classic loop-variable-capture bug and fix it three different ways","Recognize the memory-retention gotchas closures introduce, including the &quot;stale closure&quot; bug in React hooks",{"id":103,"html":104,"type":84},"b5","\u003Cp>\u003Cstrong>Who this is for:\u003C\u002Fstrong> you&#39;re comfortable declaring functions and using \u003Ccode>var\u003C\u002Fcode>\u002F\u003Ccode>let\u003C\u002Fcode>, and you&#39;ve either been bitten by a callback that saw the &quot;wrong&quot; value or you&#39;ve used \u003Ccode>useState\u003C\u002Fcode> in React and wondered why an old value showed up inside a \u003Ccode>useEffect\u003C\u002Fcode>.\u003C\u002Fp>",{"id":106,"html":107,"text":107,"type":89,"level":31},"b6","Contents",{"id":109,"type":95,"items":110,"ordered":18},"b7",[111,112,113,114,115,116,117,118,119,120,121],"\u003Ca href=\"#why-closures-matter--the-loop-that-logs-5-five-times\">Why closures matter — the loop that logs 5 five times\u003C\u002Fa>","\u003Ca href=\"#the-mental-model-a-function-with-a-backpack\">The mental model: a function with a backpack\u003C\u002Fa>","\u003Ca href=\"#stage-1-a-closure-is-just-a-function-that-remembers\">Stage 1: a closure is just a function that remembers\u003C\u002Fa>","\u003Ca href=\"#stage-2-private-state-without-a-class\">Stage 2: private state without a class\u003C\u002Fa>","\u003Ca href=\"#stage-3-memoization--a-cache-that-lives-in-a-closure\">Stage 3: memoization — a cache that lives in a closure\u003C\u002Fa>","\u003Ca href=\"#stage-4-fixing-the-loop-bug-three-ways\">Stage 4: fixing the loop bug, three ways\u003C\u002Fa>","\u003Ca href=\"#edge-cases-and-gotchas\">Edge cases and gotchas\u003C\u002Fa>","\u003Ca href=\"#best-practices-when-not-to-reach-for-a-closure\">Best practices\u003C\u002Fa>","\u003Ca href=\"#faq\">FAQ\u003C\u002Fa>","\u003Ca href=\"#cheat-sheet\">Cheat sheet\u003C\u002Fa>","\u003Ca href=\"#key-takeaways\">Key takeaways\u003C\u002Fa>",{"id":123,"html":124,"text":124,"type":89,"level":31},"b8","Why closures matter — the loop that logs 5 five times",{"id":126,"html":127,"type":84},"b9","\u003Cp>Here&#39;s the naive way to wire up five buttons, each meant to log its own position:\u003C\u002Fp>",{"id":129,"code":130,"type":131,"language":132,"highlight":133},"b10","\u002F\u002F the wrong way — every button logs the same number\nfor (var i = 1; i \u003C= 5; i++) {\n  const button = document.createElement(\"button\");\n  button.textContent = `Button ${i}`;\n  button.addEventListener(\"click\", function () {\n    console.log(`You clicked button ${i}`); \u002F\u002F always logs 5\n  });\n  document.body.appendChild(button);\n}","code","js",[],{"id":135,"html":136,"type":84},"b11","\u003Cp>Click any of the five buttons and the console prints \u003Ccode>You clicked button 6\u003C\u002Fcode>. Not \u003Ccode>1\u003C\u002Fcode>, not the button&#39;s own number — always the same value, one past the last iteration. Nothing crashed, no error was thrown, and yet the code clearly doesn&#39;t do what it looks like it does. That gap between &quot;what the code appears to say&quot; and &quot;what it actually does&quot; is the entire reason closures deserve a real mental model instead of a shrug and a workaround.\u003C\u002Fp>",{"id":138,"html":139,"type":84},"b12","\u003Cp>The fix — spoiler, it&#39;s one keyword — comes in \u003Ca href=\"#stage-4-fixing-the-loop-bug-three-ways\">Stage 4\u003C\u002Fa>. But the fix only makes sense once you know what a closure is actually holding onto, so that&#39;s where we start.\u003C\u002Fp>",{"id":141,"html":142,"text":142,"type":89,"level":31},"b13","The mental model: a function with a backpack",{"id":144,"html":145,"type":84},"b14","\u003Cp>\u003Cstrong>The mental model:\u003C\u002Fstrong> a closure is a function bundled together with a \u003Cem>live reference\u003C\u002Fem> to the variables that were in scope where the function was defined — not the values those variables held at that moment, and not a copy. Every function you write in JavaScript carries this bundle with it, always; &quot;closure&quot; isn&#39;t a special kind of function, it&#39;s a name for a function&#39;s normal relationship to its surrounding scope.\u003C\u002Fp>",{"id":147,"html":148,"type":84},"b15","\u003Cp>Picture the function as a hiker who packs a backpack the moment they&#39;re created, and the backpack holds \u003Cem>references\u003C\u002Fem> to the variables visible around it — not photographs of their current values. If someone back at camp changes a variable in the backpack after the hiker leaves, the hiker&#39;s copy of that reference still points at the same variable, so they see the new value too. The hiker doesn&#39;t carry a snapshot from the moment they left; they carry a line back to the original.\u003C\u002Fp>",{"id":150,"code":151,"type":131,"language":132,"highlight":152},"b16","function makeGreeter(name) {\n  return function greet() {\n    console.log(`Hello, ${name}`); \u002F\u002F `greet` closes over `name`\n  };\n}\n\nconst greetAda = makeGreeter(\"Ada\");\ngreetAda(); \u002F\u002F \"Hello, Ada\" — greet still has access to `name`, long after makeGreeter returned",[],{"id":154,"html":155,"type":84},"b17","\u003Cp>\u003Ccode>makeGreeter\u003C\u002Fcode> finishes executing and its call frame would normally be discarded. But \u003Ccode>greet\u003C\u002Fcode> still references \u003Ccode>name\u003C\u002Fcode>, so the JavaScript engine keeps that specific \u003Ccode>name\u003C\u002Fcode> alive for as long as \u003Ccode>greet\u003C\u002Fcode> exists. That&#39;s the whole mechanism — every stage below is this one idea, applied to a slightly different problem.\u003C\u002Fp>",{"id":157,"html":158,"text":158,"type":89,"level":31},"b18","Stage 1: a closure is just a function that remembers",{"id":160,"html":161,"type":84},"b19","\u003Cp>The clearest way to see &quot;reference, not copy&quot; is to close over a variable and then change it \u003Cem>after\u003C\u002Fem> the closure was created:\u003C\u002Fp>",{"id":163,"code":164,"type":131,"language":132,"highlight":165},"b20","function makeCounter() {\n  let count = 0;\n  return function () {\n    count += 1;\n    return count;\n  };\n}\n\nconst counterA = makeCounter();\nconst counterB = makeCounter();\n\ncounterA(); \u002F\u002F 1\ncounterA(); \u002F\u002F 2\ncounterB(); \u002F\u002F 1 — a completely separate `count`, from a separate call to makeCounter",[],{"id":167,"html":168,"type":169},"b21","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> each call to \u003Ccode>makeCounter()\u003C\u002Fcode> creates a brand-new \u003Ccode>count\u003C\u002Fcode> and a brand-new inner function closing over \u003Cem>that\u003C\u002Fem> \u003Ccode>count\u003C\u002Fcode>. \u003Ccode>counterA\u003C\u002Fcode> and \u003Ccode>counterB\u003C\u002Fcode> don&#39;t share state — closures are scoped per invocation of the outer function, not per function definition.\u003C\u002Fp>\n","quote",{"id":171,"html":172,"type":84},"b22","\u003Cp>This is also the answer to &quot;why didn&#39;t \u003Ccode>count\u003C\u002Fcode> get garbage-collected when \u003Ccode>makeCounter\u003C\u002Fcode> returned?&quot; It would have, if nothing still referenced it. Something does: the returned function. The variable&#39;s lifetime is now tied to the closure&#39;s lifetime, not to the block that declared it.\u003C\u002Fp>",{"id":174,"html":175,"text":175,"type":89,"level":31},"b23","Stage 2: private state without a class",{"id":177,"html":178,"type":84},"b24","\u003Cp>A plain object exposes its fields to anyone holding a reference to it — there&#39;s no way to stop \u003Ccode>account.balance = 1_000_000\u003C\u002Fcode> from outside. A closure gives you a place to keep state that literally cannot be reached except through the functions you choose to expose:\u003C\u002Fp>",{"id":180,"code":181,"type":131,"language":132,"highlight":182},"b25","function createAccount(initialBalance) {\n  let balance = initialBalance; \u002F\u002F not returned, not attached to anything public\n\n  return {\n    deposit(amount) {\n      balance += amount;\n      return balance;\n    },\n    withdraw(amount) {\n      if (amount > balance) throw new Error(\"insufficient funds\");\n      balance -= amount;\n      return balance;\n    },\n    getBalance() {\n      return balance;\n    },\n  };\n}\n\nconst account = createAccount(100);\naccount.deposit(50); \u002F\u002F 150\naccount.balance; \u002F\u002F undefined — there is no such property; `balance` only exists inside the closure",[],{"id":184,"html":185,"type":84},"b26","\u003Cp>There&#39;s no \u003Ccode>#balance\u003C\u002Fcode> private field syntax here, no \u003Ccode>WeakMap\u003C\u002Fcode> trick, no convention like a leading underscore that other code can ignore. \u003Ccode>balance\u003C\u002Fcode> simply isn&#39;t reachable from outside the three functions that closed over it — it never became a property of the returned object at all. This pattern (sometimes called the module pattern) predates JavaScript&#39;s class syntax and its \u003Ccode>#private\u003C\u002Fcode> fields, and it&#39;s still the right tool when you want a handful of functions to share hidden state without the ceremony of a class.\u003C\u002Fp>",{"id":187,"html":188,"text":188,"type":89,"level":31},"b27","Stage 3: memoization — a cache that lives in a closure",{"id":190,"html":191,"type":84},"b28","\u003Cp>A closure is also just a convenient place to keep a cache between calls, with no global variable and no class:\u003C\u002Fp>",{"id":193,"code":194,"type":131,"language":132,"highlight":195},"b29","function memoize(fn) {\n  const cache = new Map();\n  return function (...args) {\n    const key = JSON.stringify(args);\n    if (cache.has(key)) {\n      return cache.get(key); \u002F\u002F skip re-computing — the closure remembered\n    }\n    const result = fn(...args);\n    cache.set(key, result);\n    return result;\n  };\n}\n\nfunction slowSquare(n) {\n  for (let i = 0; i \u003C 1e8; i++); \u002F\u002F pretend this is expensive\n  return n * n;\n}\n\nconst fastSquare = memoize(slowSquare);\nfastSquare(5); \u002F\u002F slow the first time — computes and caches\nfastSquare(5); \u002F\u002F instant — the closure's `cache` already has the answer",[],{"id":197,"html":198,"type":84},"b30","\u003C!-- playground:start -->",{"id":200,"html":201,"text":201,"type":89,"level":31},"b31","🎮 Try it yourself",{"id":203,"html":204,"type":84},"b32","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweekly-javascript-closures\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":206,"html":207,"type":84},"b33","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":209,"html":210,"type":84},"b34","\u003C!-- playground:end -->",{"id":212,"html":213,"type":169},"b35","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> \u003Ccode>cache\u003C\u002Fcode> lives in exactly one place — the closure created by the single call to \u003Ccode>memoize(slowSquare)\u003C\u002Fcode> — and every call to \u003Ccode>fastSquare\u003C\u002Fcode> shares that same \u003Ccode>cache\u003C\u002Fcode> by reference. If you called \u003Ccode>memoize(slowSquare)\u003C\u002Fcode> a second time, you&#39;d get a second, independent cache, for the same reason \u003Ccode>counterA\u003C\u002Fcode> and \u003Ccode>counterB\u003C\u002Fcode> didn&#39;t share state in Stage 1.\u003C\u002Fp>\n",{"id":215,"html":216,"text":216,"type":89,"level":31},"b36","Stage 4: fixing the loop bug, three ways",{"id":218,"html":219,"type":84},"b37","\u003Cp>Back to the opening example. The bug is that \u003Ccode>var i\u003C\u002Fcode> creates exactly \u003Cstrong>one\u003C\u002Fstrong> binding for the entire loop — not one per iteration — because \u003Ccode>var\u003C\u002Fcode> is function-scoped (or global-scoped), not block-scoped. All five click handlers close over that same single \u003Ccode>i\u003C\u002Fcode>, and by the time anyone clicks a button, the loop has already finished and \u003Ccode>i\u003C\u002Fcode> is \u003Ccode>6\u003C\u002Fcode>.\u003C\u002Fp>",{"id":221,"html":222,"type":84},"b38","\u003Cp>\u003Cstrong>Fix 1 — use \u003Ccode>let\u003C\u002Fcode> instead of \u003Ccode>var\u003C\u002Fcode>.\u003C\u002Fstrong> This is the fix that shipped in ES2015 specifically to solve this problem: \u003Ccode>let\u003C\u002Fcode> creates a \u003Cem>fresh\u003C\u002Fem> binding for \u003Ccode>i\u003C\u002Fcode> on every iteration, so each closure captures its own copy of the loop variable.\u003C\u002Fp>",{"id":224,"code":225,"type":131,"language":132,"highlight":226},"b39","for (let i = 1; i \u003C= 5; i++) {\n  button.addEventListener(\"click\", function () {\n    console.log(`You clicked button ${i}`); \u002F\u002F correct — each i is its own binding\n  });\n}",[],{"id":228,"html":229,"type":84},"b40","\u003Cp>\u003Cstrong>Fix 2 — wrap the body in an IIFE to force a new scope per iteration.\u003C\u002Fstrong> This is the pre-ES2015 fix, and it&#39;s worth knowing because you&#39;ll still see it in older code: an immediately-invoked function expression creates a new function scope on every pass, and you pass the current \u003Ccode>i\u003C\u002Fcode> in as an argument, snapshotting its value at that instant.\u003C\u002Fp>",{"id":231,"code":232,"type":131,"language":132,"highlight":233},"b41","for (var i = 1; i \u003C= 5; i++) {\n  (function (capturedI) {\n    button.addEventListener(\"click\", function () {\n      console.log(`You clicked button ${capturedI}`); \u002F\u002F correct — capturedI is a fresh parameter each time\n    });\n  })(i);\n}",[],{"id":235,"html":236,"type":84},"b42","\u003Cp>\u003Cstrong>Fix 3 — pass the value as a parameter to the handler factory.\u003C\u002Fstrong> The same idea as Fix 2, expressed as a named helper instead of an inline IIFE — often the most readable option in real code:\u003C\u002Fp>",{"id":238,"code":239,"type":131,"language":132,"highlight":240},"b43","function makeHandler(n) {\n  return function () {\n    console.log(`You clicked button ${n}`); \u002F\u002F n is this call's own parameter\n  };\n}\n\nfor (var i = 1; i \u003C= 5; i++) {\n  button.addEventListener(\"click\", makeHandler(i));\n}",[],{"id":242,"html":243,"type":84},"b44","\u003Cp>All three fixes do the same thing: give each iteration its own variable to close over, instead of letting every closure share one. \u003Ccode>let\u003C\u002Fcode> just does it automatically, which is why it&#39;s the default choice today.\u003C\u002Fp>",{"id":245,"html":246,"text":246,"type":89,"level":31},"b45","Edge cases and gotchas",{"id":248,"type":95,"items":249,"ordered":18},"b46",[250,251,252,253,254],"\u003Cstrong>Closures capture by reference, not by value.\u003C\u002Fstrong> If a closure captures an object or array, mutating that object later — from anywhere, not just inside the closure — is visible to the closure the next time it runs. This is the same mechanism as the loop bug, and it cuts both ways: it&#39;s occasionally exactly what you want (Stage 2&#39;s \u003Ccode>balance\u003C\u002Fcode>), and occasionally a bug (Stage 4&#39;s \u003Ccode>i\u003C\u002Fcode>).","\u003Cstrong>Closures can retain more than you intend.\u003C\u002Fstrong> A closure keeps its \u003Cem>entire\u003C\u002Fem> enclosing scope reachable, not just the variables it uses — practically, engines are good at only keeping what&#39;s actually referenced reachable, but a closure that captures a large object (a big array, a DOM subtree) alongside a small value you meant to use will keep that large object alive for as long as the closure exists. Long-lived closures — an event listener that&#39;s never removed, a timer that never clears — are the usual place this becomes a real memory leak.","\u003Cstrong>\u003Ccode>this\u003C\u002Fcode> is not part of a closure the way ordinary variables are.\u003C\u002Fstrong> A regular \u003Ccode>function\u003C\u002Fcode> gets its own \u003Ccode>this\u003C\u002Fcode>, determined by how it&#39;s \u003Cem>called\u003C\u002Fem>, regardless of what it closes over. An arrow function has no \u003Ccode>this\u003C\u002Fcode> of its own and looks it up in its enclosing scope like any other closed-over variable — which is exactly why arrow functions are the usual choice for callbacks that need the outer \u003Ccode>this\u003C\u002Fcode>.","\u003Cstrong>React&#39;s &quot;stale closure&quot; bug is this exact mechanism.\u003C\u002Fstrong> Every render creates a fresh closure over that render&#39;s props and state. An event handler or a \u003Ccode>useEffect\u003C\u002Fcode> callback with a missing dependency closes over the \u003Cem>values from the render it was created in\u003C\u002Fem> — not the latest ones — which is why it can log or use an outdated piece of state even though the component clearly re-rendered since. The \u003Ccode>exhaustive-deps\u003C\u002Fcode> ESLint rule exists specifically to catch this.","\u003Cstrong>Closures aren&#39;t free, but they&#39;re rarely the bottleneck you&#39;d guess.\u003C\u002Fstrong> Each closure that captures variables holds a reference to its enclosing scope for the engine to manage. This matters if you&#39;re creating millions of closures in a hot loop over a huge dataset; it does not matter for ordinary UI event handlers, memoization caches, or module-pattern objects — the memory and cost are negligible at that scale.",{"id":256,"html":257,"text":257,"type":89,"level":31},"b47","Best practices: when (not) to reach for a closure",{"id":259,"html":260,"type":84},"b48","\u003Cp>\u003Cstrong>Reach for a closure when\u003C\u002Fstrong> you want state shared by a small, fixed set of functions without exposing it (Stage 2), a cache or &quot;computed once&quot; value that should persist between calls (Stage 3), or a factory that produces several independent instances of the same behavior (Stage 1&#39;s \u003Ccode>makeCounter\u003C\u002Fcode>).\u003C\u002Fp>",{"id":262,"html":263,"type":84},"b49","\u003Cp>\u003Cstrong>Avoid it when\u003C\u002Fstrong> you have many methods that all need the same shared state — a class with private \u003Ccode>#fields\u003C\u002Fcode> expresses that more clearly and with less nesting than a pile of closures returned from one factory function. Closures and classes solve the same problem; classes read better once you&#39;re past three or four methods.\u003C\u002Fp>",{"id":265,"html":266,"type":84},"b50","\u003Cp>\u003Cstrong>Watch it when\u003C\u002Fstrong> the closure lives a long time — a global event listener, a \u003Ccode>setInterval\u003C\u002Fcode> that never clears, a cache with no eviction. The closure will keep everything it references alive for exactly that long, so audit what it&#39;s actually capturing, not just what you meant it to capture.\u003C\u002Fp>",{"id":268,"html":269,"text":269,"type":89,"level":31},"b51","FAQ",{"id":271,"html":272,"text":272,"type":89,"level":43},"b52","Why does my for-loop callback always log the last value?",{"id":274,"html":275,"type":84},"b53","\u003Cp>Because \u003Ccode>var\u003C\u002Fcode> creates one binding for the whole loop, and every closure created inside the loop shares that single binding — by the time any callback runs, the loop has finished and the variable holds its final value. See \u003Ca href=\"#stage-4-fixing-the-loop-bug-three-ways\">Stage 4\u003C\u002Fa> for three fixes.\u003C\u002Fp>",{"id":277,"html":278,"text":278,"type":89,"level":43},"b54","Is a closure a copy of the variables it uses?",{"id":280,"html":281,"type":84},"b55","\u003Cp>No — it&#39;s a live reference to the variable itself, in the scope where it was declared. If that variable changes after the closure was created, the closure sees the new value, because it was never holding a snapshot in the first place.\u003C\u002Fp>",{"id":283,"html":284,"text":284,"type":89,"level":43},"b56","Do arrow functions create closures differently than regular functions?",{"id":286,"html":287,"type":84},"b57","\u003Cp>They close over ordinary variables the same way. The difference is \u003Ccode>this\u003C\u002Fcode>: an arrow function has no \u003Ccode>this\u003C\u002Fcode> of its own, so it looks \u003Ccode>this\u003C\u002Fcode> up in its enclosing scope like any other closed-over variable, while a regular function&#39;s \u003Ccode>this\u003C\u002Fcode> depends on how it&#39;s called.\u003C\u002Fp>",{"id":289,"html":290,"text":290,"type":89,"level":43},"b58","Can closures cause memory leaks?",{"id":292,"html":293,"type":84},"b59","\u003Cp>Yes, indirectly. A closure keeps its enclosing scope&#39;s variables reachable for as long as the closure itself is reachable. A closure attached to an event listener that&#39;s never removed, or stored in a cache that never evicts, holds onto whatever it captured for that entire time — that&#39;s a real, common source of leaks in long-running pages.\u003C\u002Fp>",{"id":295,"html":296,"text":297,"type":89,"level":43},"b60","What&#39;s the difference between a closure and a callback?","What's the difference between a closure and a callback?",{"id":299,"html":300,"type":84},"b61","\u003Cp>They&#39;re different concepts that usually overlap. A callback is a function passed to be called later by something else; a closure is the property that lets that function still see the variables from where it was defined. Almost every callback you write is also a closure — the two ideas describe different aspects of the same function.\u003C\u002Fp>",{"id":302,"html":303,"text":303,"type":89,"level":43},"b62","Do closures work the same way with async\u002Fawait?",{"id":305,"html":306,"type":84},"b63","\u003Cp>Yes. An \u003Ccode>async\u003C\u002Fcode> function or a \u003Ccode>.then()\u003C\u002Fcode> callback closes over its surrounding scope exactly like a synchronous one — including the loop-variable bug, if you write \u003Ccode>var\u003C\u002Fcode> inside a loop that awaits or schedules something. \u003Ccode>let\u003C\u002Fcode> fixes it the same way.\u003C\u002Fp>",{"id":308,"html":309,"text":309,"type":89,"level":31},"b64","Cheat sheet",{"id":311,"head":312,"rows":316,"type":341},"b65",[313,314,315],"Pattern","Code","Notes",[317,321,325,329,333,337],[318,319,320],"Private state","\u003Ccode>function make(){ let x; return {get(){return x}} }\u003C\u002Fcode>","\u003Ccode>x\u003C\u002Fcode> is unreachable except through the returned methods",[322,323,324],"Independent instances","call the factory again","each call creates a new scope; closures don&#39;t share across calls",[326,327,328],"Memoize","\u003Ccode>const cache = new Map()\u003C\u002Fcode> inside the factory","cache persists across calls to the returned function",[330,331,332],"Run once","\u003Ccode>let ran = false; return () =&gt; { if (ran) return; ran = true; ... }\u003C\u002Fcode>","guards side effects that must happen exactly once",[334,335,336],"Fix loop capture","use \u003Ccode>let i\u003C\u002Fcode>, not \u003Ccode>var i\u003C\u002Fcode>","gives every iteration its own binding",[338,339,340],"Pre-ES2015 loop fix","wrap in an IIFE, pass \u003Ccode>i\u003C\u002Fcode> as a parameter","manually recreates a fresh binding per iteration","table",{"id":343,"code":344,"type":131,"language":132,"highlight":345},"b66","\u002F\u002F The whole pattern, copy-paste ready: private state + memoized method + run-once init\nfunction createWidget(id) {\n  let ready = false;\n  const cache = new Map();\n\n  function init() {\n    if (ready) return; \u002F\u002F run-once guard\n    ready = true;\n    console.log(`widget ${id} initialized`);\n  }\n\n  function computeExpensive(key) {\n    if (cache.has(key)) return cache.get(key); \u002F\u002F memoized\n    const value = key.toUpperCase(); \u002F\u002F stand-in for real work\n    cache.set(key, value);\n    return value;\n  }\n\n  return { init, computeExpensive }; \u002F\u002F id, ready, and cache stay private\n}\n\nconst widget = createWidget(\"nav-1\");\nwidget.init(); \u002F\u002F logs once\nwidget.init(); \u002F\u002F no-op — ready is already true\nwidget.computeExpensive(\"hero\"); \u002F\u002F computes and caches\nwidget.computeExpensive(\"hero\"); \u002F\u002F returns the cached value",[],{"id":347,"html":348,"type":84},"b67","\u003C!-- quiz:start -->",{"id":350,"html":351,"text":351,"type":89,"level":31},"b68","🧠 Test yourself",{"id":353,"html":354,"type":84},"b69","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweekly-javascript-closures\u002Fquiz\">Take the 8-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":356,"html":357,"type":84},"b70","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":359,"html":360,"type":84},"b71","\u003C!-- quiz:end -->",{"id":362,"html":363,"text":363,"type":89,"level":31},"b72","Key takeaways",{"id":365,"type":95,"items":366,"ordered":18},"b73",[367,368,369,370,371],"A closure is a function bundled with a \u003Cem>live reference\u003C\u002Fem> to its enclosing scope — not a snapshot of the values at creation time.","Each call to an outer function creates a fresh scope; closures created in separate calls don&#39;t share state, but closures created in the \u003Cem>same\u003C\u002Fem> call do.","The classic loop bug happens because \u003Ccode>var\u003C\u002Fcode> creates one binding for the whole loop; \u003Ccode>let\u003C\u002Fcode> (or a manual IIFE) gives each iteration its own.","Closures are how you get private state and memoization without a class — but a long-lived closure keeps everything it captured alive for exactly as long as it exists.","React&#39;s &quot;stale closure&quot; bug is the same mechanism as the loop bug: a handler closing over a value from the render it was created in, not the latest one.",{"id":373,"html":374,"type":84},"b74","\u003Cp>Back to the five buttons from the top: swap that \u003Ccode>var\u003C\u002Fcode> for a \u003Ccode>let\u003C\u002Fcode>, and each one finally logs its own number — not because the bug fixed itself, but because each iteration now gets its own binding for a closure to hold onto. What&#39;s the first place in your own code you&#39;d bet a \u003Ccode>var\u003C\u002Fcode>-in-a-loop closure bug is still hiding? Tell me in the comments.\u003C\u002Fp>",{"id":376,"type":377},"b75","divider",{"id":379,"html":380,"type":84},"b76","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":382,"html":383,"type":84},"b77","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":385,"type":95,"items":386,"ordered":18},"b78",[387,388,389],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","Open five browser tabs, click a button in each, and every single one logs `5`. Not `1`, `2`, `3`, `4`, `5` — five identical `5`s, as if the loop that created the buttons never ran at all. This is one of the oldest, most reliable JavaScript bugs there is, and it isn't a bug in JavaScript — it's a closure doing *exactly* what it's supposed to do, with a variable you didn't realize you were sharing.\n\n## What you'll learn\n\nBy the end of this guide you'll be able to:\n\n- Explain what a closure actually is — a function bundled with a live reference to its scope, not a copy of it\n- Use closures to build private state without a class\n- Write a memoization cache and a \"run once\" guard using nothing but closures\n- Diagnose the classic loop-variable-capture bug and fix it three different ways\n- Recognize the memory-retention gotchas closures introduce, including the \"stale closure\" bug in React hooks\n\n**Who this is for:** you're comfortable declaring functions and using `var`\u002F`let`, and you've either been bitten by a callback that saw the \"wrong\" value or you've used `useState` in React and wondered why an old value showed up inside a `useEffect`.\n\n## Contents\n\n- [Why closures matter — the loop that logs 5 five times](#why-closures-matter--the-loop-that-logs-5-five-times)\n- [The mental model: a function with a backpack](#the-mental-model-a-function-with-a-backpack)\n- [Stage 1: a closure is just a function that remembers](#stage-1-a-closure-is-just-a-function-that-remembers)\n- [Stage 2: private state without a class](#stage-2-private-state-without-a-class)\n- [Stage 3: memoization — a cache that lives in a closure](#stage-3-memoization--a-cache-that-lives-in-a-closure)\n- [Stage 4: fixing the loop bug, three ways](#stage-4-fixing-the-loop-bug-three-ways)\n- [Edge cases and gotchas](#edge-cases-and-gotchas)\n- [Best practices](#best-practices-when-not-to-reach-for-a-closure)\n- [FAQ](#faq)\n- [Cheat sheet](#cheat-sheet)\n- [Key takeaways](#key-takeaways)\n\n## Why closures matter — the loop that logs 5 five times\n\nHere's the naive way to wire up five buttons, each meant to log its own position:\n\n```js\n\u002F\u002F the wrong way — every button logs the same number\nfor (var i = 1; i \u003C= 5; i++) {\n  const button = document.createElement(\"button\");\n  button.textContent = `Button ${i}`;\n  button.addEventListener(\"click\", function () {\n    console.log(`You clicked button ${i}`); \u002F\u002F always logs 5\n  });\n  document.body.appendChild(button);\n}\n```\n\nClick any of the five buttons and the console prints `You clicked button 6`. Not `1`, not the button's own number — always the same value, one past the last iteration. Nothing crashed, no error was thrown, and yet the code clearly doesn't do what it looks like it does. That gap between \"what the code appears to say\" and \"what it actually does\" is the entire reason closures deserve a real mental model instead of a shrug and a workaround.\n\nThe fix — spoiler, it's one keyword — comes in [Stage 4](#stage-4-fixing-the-loop-bug-three-ways). But the fix only makes sense once you know what a closure is actually holding onto, so that's where we start.\n\n## The mental model: a function with a backpack\n\n**The mental model:** a closure is a function bundled together with a *live reference* to the variables that were in scope where the function was defined — not the values those variables held at that moment, and not a copy. Every function you write in JavaScript carries this bundle with it, always; \"closure\" isn't a special kind of function, it's a name for a function's normal relationship to its surrounding scope.\n\nPicture the function as a hiker who packs a backpack the moment they're created, and the backpack holds *references* to the variables visible around it — not photographs of their current values. If someone back at camp changes a variable in the backpack after the hiker leaves, the hiker's copy of that reference still points at the same variable, so they see the new value too. The hiker doesn't carry a snapshot from the moment they left; they carry a line back to the original.\n\n```js\nfunction makeGreeter(name) {\n  return function greet() {\n    console.log(`Hello, ${name}`); \u002F\u002F `greet` closes over `name`\n  };\n}\n\nconst greetAda = makeGreeter(\"Ada\");\ngreetAda(); \u002F\u002F \"Hello, Ada\" — greet still has access to `name`, long after makeGreeter returned\n```\n\n`makeGreeter` finishes executing and its call frame would normally be discarded. But `greet` still references `name`, so the JavaScript engine keeps that specific `name` alive for as long as `greet` exists. That's the whole mechanism — every stage below is this one idea, applied to a slightly different problem.\n\n## Stage 1: a closure is just a function that remembers\n\nThe clearest way to see \"reference, not copy\" is to close over a variable and then change it *after* the closure was created:\n\n```js\nfunction makeCounter() {\n  let count = 0;\n  return function () {\n    count += 1;\n    return count;\n  };\n}\n\nconst counterA = makeCounter();\nconst counterB = makeCounter();\n\ncounterA(); \u002F\u002F 1\ncounterA(); \u002F\u002F 2\ncounterB(); \u002F\u002F 1 — a completely separate `count`, from a separate call to makeCounter\n```\n\n> **Key concept:** each call to `makeCounter()` creates a brand-new `count` and a brand-new inner function closing over *that* `count`. `counterA` and `counterB` don't share state — closures are scoped per invocation of the outer function, not per function definition.\n\nThis is also the answer to \"why didn't `count` get garbage-collected when `makeCounter` returned?\" It would have, if nothing still referenced it. Something does: the returned function. The variable's lifetime is now tied to the closure's lifetime, not to the block that declared it.\n\n## Stage 2: private state without a class\n\nA plain object exposes its fields to anyone holding a reference to it — there's no way to stop `account.balance = 1_000_000` from outside. A closure gives you a place to keep state that literally cannot be reached except through the functions you choose to expose:\n\n```js\nfunction createAccount(initialBalance) {\n  let balance = initialBalance; \u002F\u002F not returned, not attached to anything public\n\n  return {\n    deposit(amount) {\n      balance += amount;\n      return balance;\n    },\n    withdraw(amount) {\n      if (amount > balance) throw new Error(\"insufficient funds\");\n      balance -= amount;\n      return balance;\n    },\n    getBalance() {\n      return balance;\n    },\n  };\n}\n\nconst account = createAccount(100);\naccount.deposit(50); \u002F\u002F 150\naccount.balance; \u002F\u002F undefined — there is no such property; `balance` only exists inside the closure\n```\n\nThere's no `#balance` private field syntax here, no `WeakMap` trick, no convention like a leading underscore that other code can ignore. `balance` simply isn't reachable from outside the three functions that closed over it — it never became a property of the returned object at all. This pattern (sometimes called the module pattern) predates JavaScript's class syntax and its `#private` fields, and it's still the right tool when you want a handful of functions to share hidden state without the ceremony of a class.\n\n## Stage 3: memoization — a cache that lives in a closure\n\nA closure is also just a convenient place to keep a cache between calls, with no global variable and no class:\n\n```js\nfunction memoize(fn) {\n  const cache = new Map();\n  return function (...args) {\n    const key = JSON.stringify(args);\n    if (cache.has(key)) {\n      return cache.get(key); \u002F\u002F skip re-computing — the closure remembered\n    }\n    const result = fn(...args);\n    cache.set(key, result);\n    return result;\n  };\n}\n\nfunction slowSquare(n) {\n  for (let i = 0; i \u003C 1e8; i++); \u002F\u002F pretend this is expensive\n  return n * n;\n}\n\nconst fastSquare = memoize(slowSquare);\nfastSquare(5); \u002F\u002F slow the first time — computes and caches\nfastSquare(5); \u002F\u002F instant — the closure's `cache` already has the answer\n```\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweekly-javascript-closures\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n> **Key concept:** `cache` lives in exactly one place — the closure created by the single call to `memoize(slowSquare)` — and every call to `fastSquare` shares that same `cache` by reference. If you called `memoize(slowSquare)` a second time, you'd get a second, independent cache, for the same reason `counterA` and `counterB` didn't share state in Stage 1.\n\n## Stage 4: fixing the loop bug, three ways\n\nBack to the opening example. The bug is that `var i` creates exactly **one** binding for the entire loop — not one per iteration — because `var` is function-scoped (or global-scoped), not block-scoped. All five click handlers close over that same single `i`, and by the time anyone clicks a button, the loop has already finished and `i` is `6`.\n\n**Fix 1 — use `let` instead of `var`.** This is the fix that shipped in ES2015 specifically to solve this problem: `let` creates a *fresh* binding for `i` on every iteration, so each closure captures its own copy of the loop variable.\n\n```js\nfor (let i = 1; i \u003C= 5; i++) {\n  button.addEventListener(\"click\", function () {\n    console.log(`You clicked button ${i}`); \u002F\u002F correct — each i is its own binding\n  });\n}\n```\n\n**Fix 2 — wrap the body in an IIFE to force a new scope per iteration.** This is the pre-ES2015 fix, and it's worth knowing because you'll still see it in older code: an immediately-invoked function expression creates a new function scope on every pass, and you pass the current `i` in as an argument, snapshotting its value at that instant.\n\n```js\nfor (var i = 1; i \u003C= 5; i++) {\n  (function (capturedI) {\n    button.addEventListener(\"click\", function () {\n      console.log(`You clicked button ${capturedI}`); \u002F\u002F correct — capturedI is a fresh parameter each time\n    });\n  })(i);\n}\n```\n\n**Fix 3 — pass the value as a parameter to the handler factory.** The same idea as Fix 2, expressed as a named helper instead of an inline IIFE — often the most readable option in real code:\n\n```js\nfunction makeHandler(n) {\n  return function () {\n    console.log(`You clicked button ${n}`); \u002F\u002F n is this call's own parameter\n  };\n}\n\nfor (var i = 1; i \u003C= 5; i++) {\n  button.addEventListener(\"click\", makeHandler(i));\n}\n```\n\nAll three fixes do the same thing: give each iteration its own variable to close over, instead of letting every closure share one. `let` just does it automatically, which is why it's the default choice today.\n\n## Edge cases and gotchas\n\n- **Closures capture by reference, not by value.** If a closure captures an object or array, mutating that object later — from anywhere, not just inside the closure — is visible to the closure the next time it runs. This is the same mechanism as the loop bug, and it cuts both ways: it's occasionally exactly what you want (Stage 2's `balance`), and occasionally a bug (Stage 4's `i`).\n- **Closures can retain more than you intend.** A closure keeps its *entire* enclosing scope reachable, not just the variables it uses — practically, engines are good at only keeping what's actually referenced reachable, but a closure that captures a large object (a big array, a DOM subtree) alongside a small value you meant to use will keep that large object alive for as long as the closure exists. Long-lived closures — an event listener that's never removed, a timer that never clears — are the usual place this becomes a real memory leak.\n- **`this` is not part of a closure the way ordinary variables are.** A regular `function` gets its own `this`, determined by how it's *called*, regardless of what it closes over. An arrow function has no `this` of its own and looks it up in its enclosing scope like any other closed-over variable — which is exactly why arrow functions are the usual choice for callbacks that need the outer `this`.\n- **React's \"stale closure\" bug is this exact mechanism.** Every render creates a fresh closure over that render's props and state. An event handler or a `useEffect` callback with a missing dependency closes over the *values from the render it was created in* — not the latest ones — which is why it can log or use an outdated piece of state even though the component clearly re-rendered since. The `exhaustive-deps` ESLint rule exists specifically to catch this.\n- **Closures aren't free, but they're rarely the bottleneck you'd guess.** Each closure that captures variables holds a reference to its enclosing scope for the engine to manage. This matters if you're creating millions of closures in a hot loop over a huge dataset; it does not matter for ordinary UI event handlers, memoization caches, or module-pattern objects — the memory and cost are negligible at that scale.\n\n## Best practices: when (not) to reach for a closure\n\n**Reach for a closure when** you want state shared by a small, fixed set of functions without exposing it (Stage 2), a cache or \"computed once\" value that should persist between calls (Stage 3), or a factory that produces several independent instances of the same behavior (Stage 1's `makeCounter`).\n\n**Avoid it when** you have many methods that all need the same shared state — a class with private `#fields` expresses that more clearly and with less nesting than a pile of closures returned from one factory function. Closures and classes solve the same problem; classes read better once you're past three or four methods.\n\n**Watch it when** the closure lives a long time — a global event listener, a `setInterval` that never clears, a cache with no eviction. The closure will keep everything it references alive for exactly that long, so audit what it's actually capturing, not just what you meant it to capture.\n\n## FAQ\n\n### Why does my for-loop callback always log the last value?\n\nBecause `var` creates one binding for the whole loop, and every closure created inside the loop shares that single binding — by the time any callback runs, the loop has finished and the variable holds its final value. See [Stage 4](#stage-4-fixing-the-loop-bug-three-ways) for three fixes.\n\n### Is a closure a copy of the variables it uses?\n\nNo — it's a live reference to the variable itself, in the scope where it was declared. If that variable changes after the closure was created, the closure sees the new value, because it was never holding a snapshot in the first place.\n\n### Do arrow functions create closures differently than regular functions?\n\nThey close over ordinary variables the same way. The difference is `this`: an arrow function has no `this` of its own, so it looks `this` up in its enclosing scope like any other closed-over variable, while a regular function's `this` depends on how it's called.\n\n### Can closures cause memory leaks?\n\nYes, indirectly. A closure keeps its enclosing scope's variables reachable for as long as the closure itself is reachable. A closure attached to an event listener that's never removed, or stored in a cache that never evicts, holds onto whatever it captured for that entire time — that's a real, common source of leaks in long-running pages.\n\n### What's the difference between a closure and a callback?\n\nThey're different concepts that usually overlap. A callback is a function passed to be called later by something else; a closure is the property that lets that function still see the variables from where it was defined. Almost every callback you write is also a closure — the two ideas describe different aspects of the same function.\n\n### Do closures work the same way with async\u002Fawait?\n\nYes. An `async` function or a `.then()` callback closes over its surrounding scope exactly like a synchronous one — including the loop-variable bug, if you write `var` inside a loop that awaits or schedules something. `let` fixes it the same way.\n\n## Cheat sheet\n\n| Pattern | Code | Notes |\n| --- | --- | --- |\n| Private state | `function make(){ let x; return {get(){return x}} }` | `x` is unreachable except through the returned methods |\n| Independent instances | call the factory again | each call creates a new scope; closures don't share across calls |\n| Memoize | `const cache = new Map()` inside the factory | cache persists across calls to the returned function |\n| Run once | `let ran = false; return () => { if (ran) return; ran = true; ... }` | guards side effects that must happen exactly once |\n| Fix loop capture | use `let i`, not `var i` | gives every iteration its own binding |\n| Pre-ES2015 loop fix | wrap in an IIFE, pass `i` as a parameter | manually recreates a fresh binding per iteration |\n\n```js\n\u002F\u002F The whole pattern, copy-paste ready: private state + memoized method + run-once init\nfunction createWidget(id) {\n  let ready = false;\n  const cache = new Map();\n\n  function init() {\n    if (ready) return; \u002F\u002F run-once guard\n    ready = true;\n    console.log(`widget ${id} initialized`);\n  }\n\n  function computeExpensive(key) {\n    if (cache.has(key)) return cache.get(key); \u002F\u002F memoized\n    const value = key.toUpperCase(); \u002F\u002F stand-in for real work\n    cache.set(key, value);\n    return value;\n  }\n\n  return { init, computeExpensive }; \u002F\u002F id, ready, and cache stay private\n}\n\nconst widget = createWidget(\"nav-1\");\nwidget.init(); \u002F\u002F logs once\nwidget.init(); \u002F\u002F no-op — ready is already true\nwidget.computeExpensive(\"hero\"); \u002F\u002F computes and caches\nwidget.computeExpensive(\"hero\"); \u002F\u002F returns the cached value\n```\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 8-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweekly-javascript-closures\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n## Key takeaways\n\n- A closure is a function bundled with a *live reference* to its enclosing scope — not a snapshot of the values at creation time.\n- Each call to an outer function creates a fresh scope; closures created in separate calls don't share state, but closures created in the *same* call do.\n- The classic loop bug happens because `var` creates one binding for the whole loop; `let` (or a manual IIFE) gives each iteration its own.\n- Closures are how you get private state and memoization without a class — but a long-lived closure keeps everything it captured alive for exactly as long as it exists.\n- React's \"stale closure\" bug is the same mechanism as the loop bug: a handler closing over a value from the render it was created in, not the latest one.\n\nBack to the five buttons from the top: swap that `var` for a `let`, and each one finally logs its own number — not because the bug fixed itself, but because each iteration now gets its own binding for a closure to hold onto. What's the first place in your own code you'd bet a `var`-in-a-loop closure bug is still hiding? Tell me in the comments.\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":47,"canonical":392,"description":49},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweekly-javascript-closures","019fe661-0c1b-7408-8419-566729f7ae39",{"name":395,"part":396,"total":396,"items":397},"Deep Dive",5,[398,403,408,412,416],{"slug":399,"title":400,"publishedAt":401,"readingMinutes":402},"weekly-javascript-event-loop","JavaScript Event Loop Explained: The Complete Guide","2026-07-13T09:36:33.464Z",13,{"slug":404,"title":405,"publishedAt":406,"readingMinutes":407},"weekly-typescript-generics","TypeScript Generics: The Complete Guide (with Cheat Sheet)","2026-07-20T09:23:23.621Z",15,{"slug":409,"title":410,"publishedAt":411,"readingMinutes":51},"weekly-cors-explained","CORS Explained: The Complete Guide (with Cheat Sheet)","2026-08-03T09:54:26.268Z",{"slug":413,"title":414,"publishedAt":415,"readingMinutes":407},"weekly-javascript-proxy-reflect","JavaScript Proxy and Reflect: The Complete Guide","2026-08-24T07:06:37.948Z",{"slug":46,"title":47,"publishedAt":52,"readingMinutes":51},{"id":418,"locked":18},"019fe661-13c7-753e-9140-7819ad7f8404",[420],{"id":421,"slug":46,"title":422,"_count":423},"019fe776-de6c-7405-a6f8-ad7095176a46","JavaScript Closures",{"questions":424},8,[426],{"locale":13,"slug":46},{"id":421,"slug":46,"title":422,"_count":428,"questionCount":424},{"questions":424},[430,432,436,438,442,446,450,452,456,459,463,466],{"slug":67,"name":68,"articles":431},67,{"slug":433,"name":434,"articles":435},"frontend","Frontend",58,{"slug":61,"name":65,"articles":437},57,{"slug":439,"name":440,"articles":441},"css","Css",25,{"slug":443,"name":444,"articles":445},"typescript","Typescript",10,{"slug":447,"name":448,"articles":449},"performance","Performance",9,{"slug":70,"name":71,"articles":451},7,{"slug":453,"name":454,"articles":455},"grammar","Grammar",6,{"slug":457,"name":458,"articles":396},"react","React",{"slug":460,"name":461,"articles":462},"html","Html",4,{"slug":464,"name":465,"articles":462},"node","Node",{"slug":73,"name":74,"articles":462},{"items":468,"meta":590},[469,481,499,519,534,554,572],{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":470,"likeCount":19,"commentCount":19,"author":471,"vertical":472,"topic":473,"tags":474,"_count":479,"playground":480,"hasQuiz":17,"hasPlayground":17},71,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[475,476,477,478],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":73,"name":74,"color":48},{"assessments":76},{"slug":46},{"id":482,"slug":483,"title":484,"subtitle":48,"excerpt":485,"coverUrl":486,"locale":13,"readingMinutes":396,"publishedAt":487,"viewCount":488,"likeCount":19,"commentCount":19,"author":489,"vertical":490,"topic":491,"tags":492,"_count":497,"playground":498,"hasQuiz":17,"hasPlayground":17},"01a01df1-d6d5-71e8-bba6-1242e581730b","web-animations-api-imperative-control","CSS Animations Can't Pause. The Web Animations API Can.","Toggling a class to restart a CSS animation works — until you need to pause it, reverse it mid-flight, or ask how far through it is. element.animate() returns an object with those methods built in, natively, in every browser you already support.","\u002Fmedia\u002Fcovers\u002Fweb-animations-api-imperative-control.png","2026-08-25T06:55:40.108Z",40,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[493,494,495,496],{"slug":61,"name":65,"color":48},{"slug":439,"name":440,"color":48},{"slug":67,"name":68,"color":48},{"slug":433,"name":434,"color":48},{"assessments":76},{"slug":483},{"id":500,"slug":501,"title":502,"subtitle":48,"excerpt":503,"coverUrl":504,"locale":13,"readingMinutes":51,"publishedAt":505,"viewCount":506,"likeCount":19,"commentCount":76,"author":507,"vertical":508,"topic":509,"tags":512,"_count":517,"playground":518,"hasQuiz":17,"hasPlayground":17},"01a03410-2db8-735b-88b2-5285459dc915","vue-weekly-reactivity-ref-vs-reactive","Vue Reactivity Explained: ref vs reactive (+ Cheat Sheet)","A complete guide to Vue reactivity: how ref and reactive really work, why destructuring breaks updates, when to use each, and a copy-paste cheat sheet.","\u002Fmedia\u002Fcovers\u002Fvue-weekly-reactivity-ref-vs-reactive.png","2026-08-24T13:57:38.080Z",310,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":510,"name":511},"vue","Vue",[513,514,515,516],{"slug":510,"name":511,"color":48},{"slug":61,"name":65,"color":48},{"slug":70,"name":71,"color":48},{"slug":67,"name":68,"color":48},{"assessments":76},{"slug":501},{"id":520,"slug":413,"title":414,"subtitle":48,"excerpt":521,"coverUrl":522,"locale":13,"readingMinutes":407,"publishedAt":415,"viewCount":523,"likeCount":19,"commentCount":19,"author":524,"vertical":525,"topic":526,"tags":527,"_count":532,"playground":533,"hasQuiz":17,"hasPlayground":17},"019fe660-932f-704d-80f6-f8646b604799","Learn JavaScript Proxy and Reflect from the ground up — traps, invariants, and reactive state — with worked examples and a copy-paste cheat sheet.","\u002Fmedia\u002Fcovers\u002Fweekly-javascript-proxy-reflect.png",215,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[528,529,530,531],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":73,"name":74,"color":48},{"assessments":76},{"slug":413},{"id":535,"slug":536,"title":537,"subtitle":48,"excerpt":538,"coverUrl":539,"locale":13,"readingMinutes":396,"publishedAt":540,"viewCount":541,"likeCount":19,"commentCount":19,"author":542,"vertical":543,"topic":544,"tags":545,"_count":552,"playground":553,"hasQuiz":17,"hasPlayground":17},"01a013a2-ff42-738e-b6d0-cda8f338a976","import-maps-bare-specifiers-native-modules","One version bump, 40 files to edit. Import maps fix that.","Bare specifiers like import _ from 'lodash' don't work in a browser without a bundler — unless you tell the browser how to resolve them yourself, in one JSON block instead of forty hardcoded URLs.","\u002Fmedia\u002Fcovers\u002Fimport-maps-bare-specifiers-native-modules.png","2026-08-23T06:48:56.911Z",179,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[546,547,548,549],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":433,"name":434,"color":48},{"slug":550,"name":551,"color":48},"browser","Browser",{"assessments":76},{"slug":536},{"id":555,"slug":556,"title":557,"subtitle":48,"excerpt":558,"coverUrl":559,"locale":13,"readingMinutes":462,"publishedAt":560,"viewCount":561,"likeCount":19,"commentCount":19,"author":562,"vertical":563,"topic":564,"tags":565,"_count":570,"playground":571,"hasQuiz":17,"hasPlayground":17},"019ff19b-fbc7-7069-b3ba-28ffd8fbf4d3","navigator-sendbeacon-reliable-analytics","Your fetch in beforeunload is silently killed. navigator.sendBeacon guarantees delivery.","When the page unloads, the browser may cancel any in-flight fetch or XHR before it completes. navigator.sendBeacon sends a POST the browser commits to delivering, even after the document is gone.","\u002Fmedia\u002Fcovers\u002Fnavigator-sendbeacon-reliable-analytics.png","2026-08-22T06:47:21.180Z",192,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[566,567,568,569],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":433,"name":434,"color":48},{"slug":447,"name":448,"color":48},{"assessments":76},{"slug":556},{"id":573,"slug":574,"title":575,"subtitle":48,"excerpt":576,"coverUrl":577,"locale":13,"readingMinutes":455,"publishedAt":578,"viewCount":579,"likeCount":19,"commentCount":19,"author":580,"vertical":581,"topic":582,"tags":583,"_count":588,"playground":589,"hasQuiz":17,"hasPlayground":17},"01a00952-9d58-7601-89ac-42fc7bf00fc9","urlpattern-native-route-matching","Stop Writing Regex to Match URLs — The Browser Already Can","A service worker's route regex was missing one anchor and started caching live edit forms as if they were read-only pages. URLPattern, native since 2021, doesn't let you make that mistake.","\u002Fmedia\u002Fcovers\u002Furlpattern-native-route-matching.png","2026-08-20T06:53:07.359Z",237,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[584,585,586,587],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":550,"name":551,"color":48},{"slug":433,"name":434,"color":48},{"assessments":76},{"slug":574},{"page":76,"perPage":451,"total":437,"totalPages":449},"\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F the wrong way — every button logs the same number\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">for\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">var\u003C\u002Fspan> i \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1\u003C\u002Fspan>; i \u003Cspan class=\"shj-oper\">&lt;=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">5\u003C\u002Fspan>; i\u003Cspan class=\"shj-oper\">++\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> button \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> document\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">createElement\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"button\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  button\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>textContent \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-str\">`Button \u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>i\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`\u003C\u002Fspan>;\n  button\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">addEventListener\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"click\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    console\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">log\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`You clicked button \u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>i\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F always logs 5\n\u003C\u002Fspan>  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  document\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>body\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">appendChild\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>button\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">makeGreeter\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>name\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">greet\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    console\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">log\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`Hello, \u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>name\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F `greet` closes over `name`\n\u003C\u002Fspan>  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> greetAda \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">makeGreeter\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Ada\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-func\">greetAda\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F \"Hello, Ada\" — greet still has access to `name`, long after makeGreeter returned\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">makeCounter\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">let\u003C\u002Fspan> count \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">0\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    count \u003Cspan class=\"shj-oper\">+=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> count;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> counterA \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">makeCounter\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> counterB \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">makeCounter\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-func\">counterA\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F 1\n\u003C\u002Fspan>\u003Cspan class=\"shj-func\">counterA\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F 2\n\u003C\u002Fspan>\u003Cspan class=\"shj-func\">counterB\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F 1 — a completely separate `count`, from a separate call to makeCounter\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003Cdiv>22\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">createAccount\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>initialBalance\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">let\u003C\u002Fspan> balance \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> initialBalance; \u003Cspan class=\"shj-cmnt\">\u002F\u002F not returned, not attached to anything public\n\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-func\">deposit\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>amount\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n      balance \u003Cspan class=\"shj-oper\">+=\u003C\u002Fspan> amount;\n      \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> balance;\n    \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n    \u003Cspan class=\"shj-func\">withdraw\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>amount\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n      \u003Cspan class=\"shj-kwd\">if\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>amount \u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan> balance\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">throw\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Error\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"insufficient funds\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n      balance \u003Cspan class=\"shj-oper\">-=\u003C\u002Fspan> amount;\n      \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> balance;\n    \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n    \u003Cspan class=\"shj-func\">getBalance\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n      \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> balance;\n    \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> account \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">createAccount\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">100\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\naccount\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">deposit\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">50\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F 150\n\u003C\u002Fspan>account\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>balance; \u003Cspan class=\"shj-cmnt\">\u002F\u002F undefined — there is no such property; `balance` only exists inside the closure\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">memoize\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>fn\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> cache \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Map\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">...\u003C\u002Fspan>args\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> key \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">JSON\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">stringify\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>args\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-kwd\">if\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>cache\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">has\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>key\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n      \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> cache\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">get\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>key\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F skip re-computing — the closure remembered\n\u003C\u002Fspan>    \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> result \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">fn\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">...\u003C\u002Fspan>args\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    cache\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">set\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>key\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> result\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> result;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">slowSquare\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>n\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">for\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">let\u003C\u002Fspan> i \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">0\u003C\u002Fspan>; i \u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1e8\u003C\u002Fspan>; i\u003Cspan class=\"shj-oper\">++\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F pretend this is expensive\n\u003C\u002Fspan>  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> n \u003Cspan class=\"shj-oper\">*\u003C\u002Fspan> n;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> fastSquare \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">memoize\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>slowSquare\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-func\">fastSquare\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">5\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F slow the first time — computes and caches\n\u003C\u002Fspan>\u003Cspan class=\"shj-func\">fastSquare\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">5\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F instant — the closure's `cache` already has the answer\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">for\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">let\u003C\u002Fspan> i \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1\u003C\u002Fspan>; i \u003Cspan class=\"shj-oper\">&lt;=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">5\u003C\u002Fspan>; i\u003Cspan class=\"shj-oper\">++\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  button\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">addEventListener\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"click\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    console\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">log\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`You clicked button \u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>i\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F correct — each i is its own binding\n\u003C\u002Fspan>  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">for\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">var\u003C\u002Fspan> i \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1\u003C\u002Fspan>; i \u003Cspan class=\"shj-oper\">&lt;=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">5\u003C\u002Fspan>; i\u003Cspan class=\"shj-oper\">++\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>capturedI\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    button\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">addEventListener\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"click\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n      console\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">log\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`You clicked button \u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>capturedI\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F correct — capturedI is a fresh parameter each time\n\u003C\u002Fspan>    \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>i\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">makeHandler\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>n\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    console\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">log\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`You clicked button \u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>n\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F n is this call's own parameter\n\u003C\u002Fspan>  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">for\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">var\u003C\u002Fspan> i \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1\u003C\u002Fspan>; i \u003Cspan class=\"shj-oper\">&lt;=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">5\u003C\u002Fspan>; i\u003Cspan class=\"shj-oper\">++\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  button\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">addEventListener\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"click\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-func\">makeHandler\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>i\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003Cdiv>22\u003C\u002Fdiv>\u003Cdiv>23\u003C\u002Fdiv>\u003Cdiv>24\u003C\u002Fdiv>\u003Cdiv>25\u003C\u002Fdiv>\u003Cdiv>26\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F The whole pattern, copy-paste ready: private state + memoized method + run-once init\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">createWidget\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>id\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">let\u003C\u002Fspan> ready \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bool\">false\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> cache \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Map\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n  \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">init\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">if\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>ready\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F run-once guard\n\u003C\u002Fspan>    ready \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bool\">true\u003C\u002Fspan>;\n    console\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">log\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`widget \u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>id\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\"> initialized`\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n  \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">computeExpensive\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>key\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">if\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>cache\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">has\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>key\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> cache\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">get\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>key\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F memoized\n\u003C\u002Fspan>    \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> value \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> key\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">toUpperCase\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F stand-in for real work\n\u003C\u002Fspan>    cache\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">set\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>key\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> value\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> value;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> init\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> computeExpensive \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F id, ready, and cache stay private\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> widget \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">createWidget\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"nav-1\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\nwidget\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">init\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F logs once\n\u003C\u002Fspan>widget\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">init\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F no-op — ready is already true\n\u003C\u002Fspan>widget\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">computeExpensive\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"hero\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F computes and caches\n\u003C\u002Fspan>widget\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">computeExpensive\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"hero\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F returns the cached value\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":601},[]]