felix-cao / Blog

A little progress a day makes you a big success!
31 stars 4 forks source link

一道有趣的面试题(setTimeout in for loop) #94

Open felix-cao opened 5 years ago

felix-cao commented 5 years ago

Test1

for (var i = 0; i < 5; i++) {  // 注意是 i < 5, 不是 <= 5
    setTimeout(function(i) {
        console.log(i);
    }, 1000);
}
// 同时打出5个5

Test2, 现在把 setTimeout 的第二个参数改一下

for (var i = 1; i < 5; i++) {
  setTimeout(function() {
      console.log(i)
  }, i * 1000)
}
// 以一秒的频率连续输出五个5!

二、如何要它以一秒的频率分别输出 01234。

2.1、for 循环体为立即执行函数 IIFE

for(var i = 0;i<5;i ++) {
  (function(i){
    setTimeout(function() {
      console.log(i)
    }, i * 1000);
  })(i);
}

2.2、使用 ES6 中 let 关键词

for(let i = 0;i<5;i++) {
  setTimeout(function() {
    console.log(i);
  }, i * 1000);
}

for 循环头部的 let 声明还会有一个特殊的行为。这个行为指出变量在循环过程中不止被声明一次,每次迭代都会声明。随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。

2.3、bind 绑定

for (var i=0; i<5; i++) {
  setTimeout( function(i) {
    console.log(i);
  }.bind(null,i), i*1000 );
}

2.4、使用 setTimeout 的第三个参数

for (var i=0; i<5; i++) {
  setTimeout( function(i) {
    console.log(i);    
   }, i*1000, i );
}

2.5、把 setTimeout 用一个方法抽出来形成闭包

var loop = function (i) {
  setTimeout(function() {
    console.log(i);  
  }, i*1000);
};
for (var 0 = 1;i < 5; i++) {
  loop(i);
}

三、延伸

var test = function () {
  var arr = []
  for(var i = 0; i < 5; i++){
    arr.push(function () {
      return i*i
    })
  }
  return arr
}

var test1 = test()
console.log(test1[0]())
console.log(test1[1]())
console.log(test1[2]())

上面的代码在 test 函数返回一个函数数组 arr, 内部的for循环里,为 arr 数组 push 了5个函数,均为:

function () {
  return i*i
}

我们期望这里的 test1 函数数组中每个函数执行的时候,能够得到在循环时的 i 值,但是显然没能如我们所料。这是为什么呢?