You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

《JavaScript权威开发指南》循环let声明:调用内部函数如何查找变量i

Why Each Function in the Array Gets Its Own 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 i creates a fresh i variable, initialized to the current iteration’s value (0, then 1, then 2).
  • Closures capture the current i: The function func() 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 each func() “remembers” the specific block scope (and its i variable) from the iteration where it was defined.
  • Calling the function uses the captured i: When you call funcs[0](), the function first checks its own local scope for i (it doesn’t exist there). Then it climbs the scope chain to the block scope it was created in—the first iteration’s block—where i is 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 unique i value.

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:32:32