循环异步操作的影响以及其解决方案
上一次,我已经讲过闭包是如何形成的,以及它的用途。但是对于循环闭包产生的陷阱,和解决方案一笔带过啊(根本就没有)!本着不坑爹的思想,绝逼是要重新再梳理一遍。但无论如何还是要强调一下的,在闭包中会一直引用变量(是引用而不是副本),直到其断开连接不再引用,在内存中的闭包就可以得到释放。所以当我们知道这个道理之后,我们就可以开始我们今天的旅程了。
上次的例子举的不够好,竟然要操作DOM的,这次我们换个更直观的写法,用setTimeout函数。
show code!
这一次使用的是定时器setTimeout,大家都知道定时器函数执行的都是回调函数,即使那里的延时是0秒,也是等循环做完以后再输出引用的 i 值,所以自然是一直输出的是3。当然今天的重点是在如何避免出现这种情况,以及它的原理。其实我们只要对我们的代码做个非常小的变动就可以了。它的原理就是,我们手动再建立闭包,让正确的迭代值作为参数传入其中。
好,直接看看代码吧。
这里就是用了刚才说的,手动建立闭包。在回调函数外部再包裹多一层匿名函数,并用循环的i值作为参数传入,setTimeout函数引用了匿名函数的参数Index,形成闭包。但是关键是这里的闭包有三个,所以最后并不会影响正确答案。
不过,这样的写法难道不觉得有点恶心吗?!我们可以不怕麻烦的,用我上一篇闭包说过的,用个辅助函数稍微简化下,但原理是一样的。
我们从这两个例子里,可以很明确的看到,回调函数不再是直接引用循环的变量,而是引用了新的父函数(包裹函数)的参数。这样我们就可以避开循环造成的闭包陷阱了。当然我最后还是留了一个大招,ECMAScript5 的方法,forEach!
我们简单了解一下forEach(《JavaScript高级程序设计》96页,有详细解释)。forEach可以接收两个参数,一个是要循环执行的函数,另一个是指定执行该函数的对象改变this的值(可选参数)。当然重点在第一个参数,也就是要执行循环的函数,它会被传入三个参数,当前的值item,在数组的位置index 和数组对象本身array。
forEach其中有一个参数是当前的位置index,我告诉你,大胆放心的用!看代码!
干净利索!连for循环都木有了。这个方法可以让我们不用再考虑手动建立闭包的事,非常的酷炫!
但是要强调两点:
1、这个方法是不能用在古董浏览器的(IE6-8),但是我们可以自行在数组的prototype里拓展呀!
2、这个方法仅仅是数组对象的方法,对于类数组对象(如nodeList之流的)是无法直接使用的,但是我们可以Array.prototype.forEach.call呀!!
对于call的作用不是很理解的同学,可以看看我的另一篇博客http://www.cnblogs.com/YikaJ/p/4113831.html,只要用心思考过,想必是可以帮助你更好的掌握call和apply的!
原谅我,现在七点,我还没吃饭,我得赶紧发完这篇blog吃饭去啦~!