for循环与setTimeout、事件绑定

2016-11-09  本文已影响0人  iluvleleyo

闭包

闭包 = 函数 + 创建该函数的环境

问题

//循环中为不同的元素绑定事件,事件回调函数里如果调用了跟循环相关的变量,则这个变量取循环的最后一个值
for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
for (var i = 0; i < 5; i++) {
    setTimeout(function(){
        console.log(i)
    },10)
}

这些问题出现的原因,闭包不是主因,是由于setTimeout和事件绑定的机制造成的。
setTimeout是从任务队列结束的时候开始计时的,如果前面有进程没有结束,那么它就等到它结束再开始计时。在这里,任务队列就是它自己所在的循环。循环结束setTimeout才开始计时,所以无论如何,setTimeout里面的i都是最后一次循环的i。

解决方案

setTimeout

setTimeout第一个参数需要一个函数,所以返回一个函数给它,返回的同时把i作为参数传进去,通过形参v缓存了i,并带进返回的函数里面。(算是闭包)

for (var i = 0; i < 5; i++) {
    var a = function(v){
        return function(){
            console.log(v)
        }
    }
    setTimeout(a(i),0)
}
for (var i = 0; i < 5; i++) {
    (function(j){
        setTimeout(function(){
            console.log(j);
        },0)
    })(i);
}

解决方法还可以用let——使用 let 来声明块变量,这时候变量就能作用于这个块所以能够保存下来。

let翻译

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

// babel翻译之后
var _loop = function _loop(i) {
    setTimeout(function () {
        console.log(i);
    }, 10);
};

for (var i = 0; i < 5; i++) {
    _loop(i);
}

绑定事件

for (var i = 0; i < 5; i++) { 
    var a = function(){
        console.log(i) 
    }
    document.body.addEventListener('click',a) 
}

事件是需要触发的,而绝大多数情况下,触发的时候循环已经结束了,所以循环相关的变量就是最后一次的取值。
为了解决,通过函数的实参传进函数体。

for (var i = 0; i < 5; i++) { 
    var a = function(v){
        return function(){
            console.log(v)
        }
    }
    document.body.addEventListener('click',a(i))
}
上一篇 下一篇

猜你喜欢

热点阅读