《JavaScript权威开发指南》循环let声明:调用内部函数如何查找变量i
i When Using let in Loops Great question—this is one of those JavaScript behaviors that feels magical until you break down how block scoping and closures work together. Let’s walk through this step by step, with examples to make it concrete.
First, let’s contrast with var to highlight why let is different. If you used var in the loop:
const funcs = []; for (var i = 0; i < 3; i++) { funcs.push(function func() { console.log(i); }); } funcs[0](); // 3 funcs[1](); // 3 funcs[2](); // 3
All three functions log 3 because var declares i in the global or outer function scope (not per loop iteration). Every function in funcs references the same i variable, which ends up as 3 once the loop finishes.
Now, with let:
const funcs = []; for (let i = 0; i < 3; i++) { funcs.push(function func() { console.log(i); }); } funcs[0](); // 0 funcs[1](); // 1 funcs[2](); // 2
This works because let has block-level scoping, and loops have a special rule for let declarations: each iteration creates a new, separate binding of i. Here’s what’s happening under the hood:
- Per-iteration block scopes: Every time the loop runs, the
{ ... }of the loop acts as a new block scope. Inside this block,let icreates a freshivariable, initialized to the current iteration’s value (0, then 1, then 2). - Closures capture the current
i: The functionfunc()you push into the array is defined inside this per-iteration block. Functions in JavaScript form closures—they retain a reference to the scope they were created in. So eachfunc()“remembers” the specific block scope (and itsivariable) from the iteration where it was defined. - Calling the function uses the captured
i: When you callfuncs[0](), the function first checks its own local scope fori(it doesn’t exist there). Then it climbs the scope chain to the block scope it was created in—the first iteration’s block—whereiis 0. That’s the value it logs. Same for the other functions: each looks up to its own iteration’s block scope to find its uniqueivalue.
To put it simply: each func() doesn’t just store a value of i—it stores a reference to the specific i variable that existed when the function was made. Since let gives you a new i every loop turn, each function gets its own copy to reference later.
If you’ve used ES5, this is exactly what we used to simulate with immediately-invoked function expressions (IIFEs):
const funcs = []; for (var i = 0; i < 3; i++) { (function(localI) { funcs.push(function func() { console.log(localI); }); })(i); } funcs[0](); // 0
The IIFE creates a new scope for each iteration, and localI acts as the per-iteration variable—let just makes this pattern built into the language, no extra syntax needed.
内容的提问来源于stack exchange,提问作者juanli

