js 循环 i 值问题

var Greeters = []
for (var i = 0 ; i < 10 ; i++) {
Greeters.push(function () { return console.log(i) })
}
Greeters[0]() // 10
Greeters[1]() // 10
Greeters[2]() // 10

var Greeters = []
for (var i = 0 ; i < 10 ; i++) {
Greeters.push(i)
}
Greeters[0] // 0
Greeters[1] // 1
Greeters[2] // 2
看了for循环问题和函数形参问题 这个提问 自己试了下,还不是很懂,
为什么第一段代码 是 10 10 10 第二段代码又变成了 0 1 2
求大佬 通俗点的解释,打个比方 举个栗子.

阅读 5.3k
7 个回答
  1. 房子里有一个i和10个小盒子,随着i的变大,往10个小盒子里都塞了一个function () {console.log(i)}的函数,那么执行的时候就会去找i,可是你又没有把i的值传进函数,函数内里找不到i的值,所以会去外面房子里找,这个时候i已经是10了。
  2. 房子里有一个i和10个小盒子,随着i的变大,你把i当时的值直接塞进了小盒子里。
  3. let的则是,房子里有10个屋子,你在屋子里声明了一个变大的i,然后又将function () {console.log(i)}塞到了一个小盒子里,执行的时候去找i,函数里没i的值,会在屋子里找到各自的i,所以分别是0-9。

总结下就是:

  1. 房子跟屋子都是作用域。小盒子对应你Greeters数组的每一项。作用域链是函数->(屋子)->房子。
  2. 函数中变量的值是在执行时确定的,作用域链是在声明时确定的,会沿着作用域从内往外找。
  3. let会形成一个作用域。

改了答案,刚发现写着写着把小盒子的设定弄错了。

第一个 并不是push了 1 23 这些数字 而是push了一个函数对象
他push了是9个相同的 函数 你每次是需要调用 函数 通过数组索引访问到函数 然后调用函数
也就是小括号作用 所以每次都是输出10
每次调用for 循环是瞬间执行完的 所以他只会输出 10

第二次是把1 2 3 4这些数字push 到数组里面 所以 你按索引访问就是对应的每个数字

你有10个苹果往箱子里装,装之前你需要写上现在是第几个,写字其实就相当与一个方法,然后放箱子里.
区别就是第一个你放进箱子里的是一个写字的方法(function () { return console.log(i) }) 你并没有写,因为他没执行.
等你十个都放进去了之后,你拿出来一个要写字了(就是要执行这个方法了),但是现在已经十个都放进去了,已经操作到第十个了,现在就是第10个,你拿哪个出来写个字不都是10么.

第二种就是你先写了字,在放进去.

又是可爱的我。
举例子不会,但是我可以告诉你要想清楚哪些问题。
其次,这个问题的主要是三点,一个是let和var的区别,第二是for循环的问题,第三是涉及到了函数形参与实参的问题。
先说第一个问题,let和var都是用来声明局部变量,既然叫局部变量,想必你也知道,在当前代码块结束之后,这个变量就不能用了。但是不能用是不能用了,let声明的变量系统就直接把变量地址和值都回收了(就像删了,放进回收站了),但是var声明的变量系统还保留了它的地址和值。
第二个问题,注意到for循环中function的问题,要想清楚function()这个操作是没有做值的传递,换句话说,里面的变量是调用外层的变量值(相当于对于该函数的全局变量)。
第三个问题重中之重,函数调用时,引入函数主体中的形参实际上是你放入()中的实参的一个复制。

第一种是往Greeters push 了一个函数,而for循环中的i是用var声明的,var声明变量没有块级作用域,这里的i相当于全局变量,当调用函数的时候for循环里的i已经变成了10,所以打印的都是10

1.

var Greeters = []
for (var i = 0 ; i < 10 ; i++) {
Greeters.push(function () { return console.log(i) })
}
Greeters[0]() // 10
Greeters[1]() // 10
Greeters[2]() // 10

其中var i = 0定义了变量i值为0,假设其地址是addr0。在for的过程中i的值不断从0~9变化,每变化一次他的存储地址跟着变化一次,完成了整个for的过程i指向addr9,最后var i = 10,判断超出i<10,不执行for,此时i最终指向addr10,这个时候不管怎么引用函数,i的存储地址都是addr10其值为10。
2

var Greeters = []
for (var i = 0 ; i < 10 ; i++) {
Greeters.push(i)
}
Greeters[0] // 0
Greeters[1] // 1
Greeters[2] // 2

这里推向Greeters的不是变量,而是确切的数字,所以其指向地址不改变其值不改变。

看错了重新答
我看了链接 链接的问题是变量作用域问题
js中只有函数内有局部作用域
链接的第二个循环是let这是es6新的声明局部变量
第一种调用Greeters[0]的时候是10是因为调用的时候才从全局取值 这时候i已经循环完毕保持为10
第二种调用Greeters[0]的时候是0是因为调用的时候是先从局部作用域取值所有i为0


你的第二个是直接把值0放进数组里调用的时候直接取值0

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题