Closures Capture the Variable, Not the Value in JavaScript
A closure holds a reference to the variable, not a snapshot of the value. Here is why this matters.
Closures Capture the Variable, Not the Value in JavaScript
A common misconception: closures capture a snapshot of the value. They do not. They hold a reference to the variable. If the variable changes, the closure sees the updated value.
The Evidence
let x = 1; function getX() { return x; } getX(); // 1 x = 2; getX(); // 2 (not 1) `` `getX` closes over the variable `x`, not the value `1`. When `x` changes, `getX` returns the new value. ### The Loop Bug Explained ```js for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); } // logs 3, 3, 3 `` All three callbacks close over the **same variable** `i`. They do not capture the value at the time of closure creation. When they run, `i` is 3, so they all log 3. ### How to Capture the Value #### Method 1: `let` (Fresh Binding) ```js for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); } // logs 0, 1, 2 `` `let` creates a new variable per iteration. Each closure captures a different variable. #### Method 2: IIFE (New Scope Per Iteration) ```js for (var i = 0; i < 3; i++) { (function (j) { setTimeout(() => console.log(j), 0); })(i); } // logs 0, 1, 2 `` The IIFE's parameter `j` is a new variable. The value of `i` is **passed by value** (primitives are copied). The closure captures `j`, not `i`. #### Method 3: `setTimeout` Extra Arguments ```js for (var i = 0; i < 3; i++) { setTimeout((j) => console.log(j), 0, i); } // logs 0, 1, 2 `` The extra argument `i` is passed by value to the callback's parameter `j`. ### Objects Are Different For objects, the closure captures a reference to the object. Mutating the object affects the closure: ```js let obj = { count: 0 }; function getCount() { return obj.count; } obj.count = 5; getCount(); // 5 (mutation is visible) `` But reassigning the variable does not affect the closure's view of the original object? Actually, it does, because the closure references the variable: ```js let obj = { count: 0 }; function getObj() { return obj; } obj = { count: 99 }; getObj(); // { count: 99 } (the variable changed) `` ### The Takeaway Closures capture a reference to the variable, not a snapshot of the value. If the variable changes, the closure sees the updated value. This is why the `var` loop bug logs the final value. To capture the value, use `let` (fresh binding), an IIFE (new scope with pass-by-value parameter), or `setTimeout` extra arguments.
The variable. A closure holds a reference to the variable, not a snapshot of its value. If the variable changes before the callback runs, the closure sees the updated value. This is why var in a loop logs the final value.
Because they all close over the same variable i. Closures capture the variable, not the value. When the callbacks run, i is the final value, so all callbacks log it.
Use let (creates a fresh binding per iteration), or an IIFE that passes the current value as a parameter (pass-by-value for primitives), or pass the value as an extra argument to setTimeout.
It captures a new parameter (j), which is a copy of the value (for primitives). The closure inside the IIFE closes over j, not the outer i. So even if i changes later, j holds the value at the time of the IIFE call.
By reference to the variable. If you mutate the object's properties, the closure sees the changes. If you reassign the variable to a new object, the closure sees the new object (because it references the variable, not the original object).
Ready to master React completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master React
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

