上一次,我已經講過閉包是如何形成的,以及它的用途。但是對於循環閉包產生的陷阱,和解決方案一筆帶過啊(根本就沒有)!本着不坑爹的思想,絕逼是要重新再梳理一遍。但無論如何還是要強調一下的,在閉包中會一直引用變量(是引用而不是副本),直到其斷開連接不再引用,在內存中的閉包就可以得到釋放。所以當我們知道這個道理之后,我們就可以開始我們今天的旅程了。
上次的例子舉的不夠好,竟然要操作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吃飯去啦~!