为何两段仅变量名不同的JavaScript代码输出结果不同?
为什么两段看似相似的代码输出不同?
嘿,这个问题其实藏着一个很经典的var作用域陷阱,我来给你拆解清楚——根本不是funcs和fun的变量名差异导致的,而是第二个循环用的变量不一样!
先直接说两段代码的实际输出:
- Sample 1会连续输出三次
My value: 3 - Sample 2会依次输出
values is 0、values is 1、values is 2
核心原因:var的函数级作用域特性
var声明的变量是函数级作用域,没有块级作用域——也就是说,不管你在多少个for循环里写var i,只要在同一个函数(这里是全局函数)里,它们都是同一个变量。
先看Sample 1的执行流程
- 全局作用域里会提前声明所有
var变量:funcs、i、j(这就是变量提升) - 第一个
for循环:i从0走到2,每次给funcs[i]赋值一个匿名函数。注意:这个函数里的console.log引用的是全局的i变量,不是循环里的局部变量 - 第一个循环结束后,
i的值会变成3(因为i++后不满足i<3,退出循环) - 第二个
for循环用的是独立的j,从0走到2,每次调用funcs[j]()。这时候函数里引用的i还是全局的那个i,值是3,所以三次输出都是My value: 3
再看Sample 2的执行流程
- 全局作用域里提前声明
fun和i(两个for循环里的var i都是同一个全局变量,重复声明不会创建新变量) - 第一个
for循环结束后,i的值同样是3 - 第二个
for循环开始:直接给全局的i重新赋值为0(var i在这里只是重复声明,不起作用) - 每次调用
fun[i]()的时候,函数里引用的i是当前全局的i——也就是第二个循环的当前索引值:- 第一次调用时
i=0,输出values is 0 - 第二次调用时
i=1,输出values is 1 - 第三次调用时
i=2,输出values is 2
- 第一次调用时
如何让两段代码输出一致?
如果想让Sample 1也输出0、1、2,有两种常用方法:
- 用ES6的
let声明循环变量(let有块级作用域,每个循环迭代都会创建独立的变量):
var funcs = []; for (let i = 0; i < 3; i++) { funcs[i] = function() { console.log("My value: " + i); }; } for (var j = 0; j < 3; j++) { funcs[j](); }
- 用立即执行函数(IIFE)创建局部作用域:
var funcs = []; for (var i = 0; i < 3; i++) { (function(currentI) { funcs[currentI] = function() { console.log("My value: " + currentI); }; })(i); } for (var j = 0; j < 3; j++) { funcs[j](); }
内容的提问来源于stack exchange,提问作者iJade
相关产品推荐
相关产品推荐

