LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

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

zhenglin
2026年4月1日 16:12 本文热度 64

问题代码

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

function f() {

    for(var i = 0; i < 10; i++) {

        setTimeout(() => {

            console.log('i=', i)

        });

    }

}


f();

输出结果:

i= 10

i= 10

i= 10

...(共 10 次)

执行过程详解

第一步:var 变量的作用域

for(var i = 0; i < 10; i())

    ↑

    └── var 声明的变量是函数作用域

        整个函数 f 内都能访问这个 i

第二步:循环执行过程

代码高亮:

循环次数     i 的值    循环条件 (i < 10)

---------------------------------------

第 1 次      0         ✓ 通过

第 2 次      1         ✓ 通过

...

第 10 次     9         ✓ 通过

             10        ✗ 不通过,循环结束


循环结束后:i = 10

第三步:创建 10 个回调函数

for(var i = 0; i < 10; i++) {

    setTimeout(() => {

        console.log('i=', i)  // ← 所有回调共享同一个 i

    });

}

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

第四步:异步执行时序


时间轴:

─────────────────────────────────────────

| 同步执行阶段        | 异步执行阶段        |

─────────────────────────────────────────

for 循环完成      setTimeout 回调执行

i 递增到 10       读取 i 的值(此时 i=10)

                  输出 10 次 i=10

─────────────────────────────────────────

核心原因

三个关键点

  1. var 是函数作用域

    • 不是块级作用域

    • 整个函数内只有一个 i 变量


  2. 闭包共享变量

    • 10 个箭头函数都引用同一个 i

    • 不是创建 10 个独立的 i 副本


  3. setTimeout 异步执行

    • 回调函数放入任务队列延迟执行

    • 执行时循环已结束,i 已经是 10


图示理解:

变量 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(推荐)✨

代码高亮:

function f() {

    for(let i = 0; i < 10; i++) {

        setTimeout(() => {

            console.log('i=', i)

        });

    }

}

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

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


方案 2:IIFE 立即执行函数

function f() {

    for(var i = 0; i < 10; i++) {

        (function(j) {

            setTimeout(() => {

                console.log('i=', j)

            });

        })(i);

    }

}

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


方案 3:传递参数给 setTimeout

function f() {

    for(var i = 0; i < 10; i++) {

        setTimeout((j) => {

            console.log('i=', j)

        }, 0, i);

    }

}

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


知识点总结

一句话总结

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

 

参考文章:原文链接


该文章在 2026/4/1 16:12:58 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved