js的閉包是老生常談的問題了,網上大部分的解釋都不是特別易懂。在我看來,解決一個碰到的問題有兩個思路:一是找到解決這個問題的方法。二是嘗試從根源上去解析這個問題,以避免被其他類似的問題困擾。
下面我說說第一種思路。
咱們先看一個簡單的例子:
var arr=[];
function eg(){
for (var i= 0;i<3;i=i++){
arr[i]=function(){
alert(i)
}
}
};
arr();
正常情況下,我們希望能按照for循環的順序依次彈出0,1,2。但實際上卻是彈出了3次3。爲什麼會發生這種情況,咱們在這暫且不談,在這個思路里只談咱們解決這種情況,那就是加上立即執行函數。
var arr=[];
function eg(){
for (var i= 0;i<3;i=i++){
(function(){
var temp = i;
arr[i]=function(){
alert(temp); //彈出0,1,2,3
})();
}
};
arr();
之後它就能正確達到咱們想要的效果了。如果你有一批動態生成的div或者其他元素,那麼就可以在aarr[i]=function(){}這個方法裏用jquery動態改變元素,或者說實現,點擊某個div,再行後臺獲取這個div對應的值來渲染到這個div中,比如說:
var arr=[];
function eg(){
for (var i= 0;i<3;i=i++){
(function(){
var temp = i;
arr[i]=function(){
alert(temp); //彈出0,1,2,3
$("#id" + p).css("color", "white");
})();
}
};
arr();
ok,現在來談談第二種思路。想要解析閉包的根源,就不得不談談js的幾個重要的特性:作用域鏈以及內存回收機制。
什麼是作用域鏈?
簡單來說,就是當函數在一個環境中執行時,會創建變量對象的一個作用域鏈。作用域鏈的用途是保證對執行環境有權訪問的所有變量和函數的有序訪問。何爲有序?比如說有三個for循環,他們在各自的循環裏都定義了一個局部變量,假設從外到內是a,b,c。對於最裏面的那層循環來說,它的作用域鏈就是 c到b到a到全局變量。
什麼是內存回收機制?
和java一樣javascript也是自動回收機制,把不再使用的內存回收。什麼情況下不再使用呢?比如說在一個函數中定義的局部變量,在這個函數執行完畢後它就被銷燬了。但是當你在一個函數中定義了另一個匿名函數,並在裏面定義了一個局部變量,在這個匿名函數外部又調用了這個內部的局部變量,這時回收機制就不會生效,直到這個閉包徹底失效。
所以閉包的根本用途,其實就在於讓一個函數內部的函數可以調用上一層環境聲明的局部變量,方法就是立即執行函數。
有趣的是,在我遇到這個問題不就後我又碰到一個類似的情況,
那就是settimeout( )函數。使用場景是,處於某種需要,我想將一個for循環遍歷的時間縮短,然後直接在for中加入了settimeout後發現打印的是亂碼,這與之前的閉包問題有異曲同工之妙。
爲什麼會這樣呢?因爲js是單線程的執行順序,而settimeout()卻是一個異步的方法。如下:
var a=[1,2,3];
var len=a.length;
for(i=0;i<len;i++){
setTimeout{function(){
console.log(i);
},100}
}
會打印3個undefined。但是修改一下代碼
var a=[1,2,3];
var len=a.length;
for(i=0;i<len;i++){
setTimeout{function(){
console.log(i);
},**i*100**}
}
就可以正確的log出來了。
兩種思路沒有孰優孰劣,只要能帶給你思考,就值得肯定。