这段js的congsole.log(i)的i是哪儿来的呢

for (var i = 3; i < 4; i++) {
  data[i] = function () {
    console.log(i);
  };
}
data[3]()// 打印结果为 4;
// console.log(data[3]());

输出是4,但是为什么不是输出3呢,所以console.log里面的i是怎么变成4的呢,for循环里面的i最大不就是3吗

阅读 3.6k
8 个回答

通俗点讲,data 这个数组,在 for 循环中被添加了一个元素(因为 i 初始值为 3,并且 < 4,所以只会执行循环体一次),这个元素是一个 function。相信这一点,题主是明白的。

而且,因为 i == 3,所以,这个元素的实际索引是 3,也就是 data[3] 是一个 function

i 初始为 3,且该循环体执行了一次。要知道,循环体执行结束后,会执行一次 i++,然后才会去判断 i < 4 是否成立。也就是说,此时 i 已经等于 4 了。这样的话,答案已经比较明显了:此时执行 data[3](),输出的 i 自然也就是 4 了。

PS:data[3]() 输出是 4,但是,console.log(data[3]()) 会输出 undefined。因为 data[3] 这个 function 并没有 return 任何值。

for循环的最后一次,变成了4,你再打印i, 不就是4了吗

打印的 i 来自于全局作用域。
要彻底理解这个问题,需要理清两个知识点。

  • ES5 只有全局作用域和函数作用域,没有块级作用域。
  • 闭包。

网上关于这两者的介绍有很多,可以搜索一下仔细看看。

data[i] 它只是一个函数,它的功能是打印i,退出循环的时候i已经变成4了,所以当你调用data[3]时,就是打印出i,所以打印出4

改成这样呢?

var data = [];
for (var i = 3; i < 4; i++) {
  data[i] = i;
}
console.log(data[3])

当前的 i 是一个全局变量,循环的本质是到条件不满足才会结束 i=3 时仍然符合条件,进入了循环,这时又执行了 i++ 变成了 4,4 不符合条件,所以不再进入循环,i 停留在了 4 上。所以这里 console.log(i) 都会是 4。那为什么中间的量没有保存呢,是因为这里中间的变量没有形成闭包,稍微修改
,用 IIFE 形成一个闭包,就可以输出 3 了。

for (var i = 0; i < 4; i++) {
  data[i] = (function(j) {return function() { console.log(j) }})(i)
}
console.log(data[3]()); // 输出 3
//没有块作用域    
if(true){
  var color='red'
} 
console.log(color) //"red"
    
// i=3 会执行,再继续i++ 后,i<4 不成立,因为此时的i=4
for (var i = 3; i < 4; i++)

为什么没人提到let呢,全都在说循环退出时i的值,只有一个人提到了作用域。不过我一直没有特别注意作用域或闭包的问题,也一直用得好好的。如果你把var改成let,输出就会是3了。如果是var的话,通过这个循环往data里放的4个函数所打印的i是同一个i,也就是你在for循环初始化循环变量时声明那一个i。你可以尝试以下代码:

for(let i = 0; i < 4; i++) console.log(i)
console.log(i)  // ReferenceError: i is not defined
for(var i = 0; i < 4; i++) console.log(i)
console.log(i)  // 4
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏