首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >JavaScript 闭包经典问题:为什么输出 10 次 i=10

JavaScript 闭包经典问题:为什么输出 10 次 i=10

原创
作者头像
用户12278826
发布2026-03-31 09:50:18
发布2026-03-31 09:50:18
860
举报
文章被收录于专栏:程序员分享程序员分享

JavaScript 闭包经典问题:为什么输出 10 次 i=10

问题代码

先观察以下代码,思考输出结果:

代码语言:javascript
复制
javascript 体验AI代码助手 代码解读复制代码function f() {
    for(var i = 0; i < 10; i++) {
        setTimeout(() => {
            console.log('i=', i)
        });
    }
}

f();

输出结果:

代码语言:javascript
复制
ini 体验AI代码助手 代码解读复制代码i= 10
i= 10
i= 10
...(共 10 次)

执行过程详解

第一步:var 变量的作用域
代码语言:javascript
复制
css 体验AI代码助手 代码解读复制代码for(var i = 0; i < 10; i())
    ↑
    └── var 声明的变量是函数作用域
        整个函数 f 内都能访问这个 i
第二步:循环执行过程
代码语言:javascript
复制
markdown 体验AI代码助手 代码解读复制代码循环次数     i 的值    循环条件 (i < 10)
---------------------------------------
第 1 次      0         ✓ 通过
第 2 次      1         ✓ 通过
...
第 10 次     9         ✓ 通过
             10        ✗ 不通过,循环结束

循环结束后:i = 10
第三步:创建 10 个回调函数
代码语言:javascript
复制
css 体验AI代码助手 代码解读复制代码for(var i = 0; i < 10; i++) {
    setTimeout(() => {
        console.log('i=', i)  // ← 所有回调共享同一个 i
    });
}

每次循环创建一个箭头函数,都通过闭包引用变量 i

第四步:异步执行时序
代码语言:javascript
复制
css 体验AI代码助手 代码解读复制代码时间轴:
─────────────────────────────────────────
| 同步执行阶段        | 异步执行阶段        |
─────────────────────────────────────────
for 循环完成      setTimeout 回调执行
i 递增到 10       读取 i 的值(此时 i=10)
                  输出 10 次 i=10
─────────────────────────────────────────

核心原因

三个关键点
  1. var 是函数作用域
    • 不是块级作用域
    • 整个函数内只有一个 i 变量
  2. 闭包共享变量
    • 10 个箭头函数都引用同一个 i
    • 不是创建 10 个独立的 i 副本
  3. setTimeout 异步执行
    • 回调函数放入任务队列延迟执行
    • 执行时循环已结束,i 已经是 10
图示理解
代码语言:javascript
复制
less 体验AI代码助手 代码解读复制代码变量 i 的生命周期:
─────────────────────────────→ 时间
     0 1 2 3 4 5 6 7 8 9  10
     └───┬───┘ └───┬───┘
         │                │
     同步循环执行        循环结束
                        i=10

回调函数 1:  ────────────────────→ 读取 i (10)
回调函数 2:  ────────────────────→ 读取 i (10)
...
回调函数 10: ────────────────────→ 读取 i (10)

解决方案

方案 1:使用 let(推荐)✨
代码语言:javascript
复制
javascript 体验AI代码助手 代码解读复制代码function f() {
    for(let i = 0; i < 10; i++) {
        setTimeout(() => {
            console.log('i=', i)
        });
    }
}

原理: let 是块级作用域,每次循环创建新的 i 绑定

输出: i= 0i= 9 各一次


方案 2:IIFE 立即执行函数
代码语言:javascript
复制
javascript 体验AI代码助手 代码解读复制代码function f() {
    for(var i = 0; i < 10; i++) {
        (function(j) {
            setTimeout(() => {
                console.log('i=', j)
            });
        })(i);
    }
}

原理: 通过函数参数保存每次循环的 i 值


方案 3:传递参数给 setTimeout
代码语言:javascript
复制
javascript 体验AI代码助手 代码解读复制代码function f() {
    for(var i = 0; i < 10; i++) {
        setTimeout((j) => {
            console.log('i=', j)
        }, 0, i);
    }
}

原理: setTimeout 的第三个参数会传递给回调函数


知识点总结

概念

说明

var 作用域

函数作用域,非块级作用域

let 作用域

块级作用域,每次循环创建新绑定

闭包

函数可以访问其声明时所在作用域的变量

异步

setTimeout 的回调会延迟执行

共享引用

同一作用域的闭包引用同一个变量


一句话总结

var 的函数作用域 + 闭包共享变量 + setTimeout 异步执行 = 所有回调读取到循环结束后的同一个 i 值(10)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JavaScript 闭包经典问题:为什么输出 10 次 i=10
    • 问题代码
    • 执行过程详解
      • 第一步:var 变量的作用域
      • 第二步:循环执行过程
      • 第三步:创建 10 个回调函数
      • 第四步:异步执行时序
    • 核心原因
      • 三个关键点
      • 图示理解
    • 解决方案
      • 方案 1:使用 let(推荐)✨
      • 方案 2:IIFE 立即执行函数
      • 方案 3:传递参数给 setTimeout
    • 知识点总结
    • 一句话总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档