閉包概念集合


一、創建閉包

創建閉包的常見方式,就是在一個函數內部創建另一個函數。

 

 

二、作用域鏈

當某個函數被調用的時候,會創建一個執行環境和相應的作用域鏈,然后使用arguments初始化對象。這個對象叫做活動對象。

在作用域鏈中,外部函數的活動對象始終處於第二位。以此類推,直到作用域鏈終點——全局執行環境。 首先,讓我先來看看什么叫做活動對象。

function compare(a,b){
    if(a>b){
        return true;
    }else if(a<b){
        return false;
    }else{
        return 0;
    }
}
var result=compare(5,10);//false
console.log(result);

 

這段代碼中,首先定義了compare()函數,然后在全局作用域中調用了它。在調用這個函數的時候,創建了arguments、a、b這三個活動對象。他們處在作用域鏈的第一位。

而result、compare被稱為變量對象,他們處於全局執行環境下,在作用域鏈中處於第二位。

全局環境的變量對象始終存在,在函數中訪問一個變量時,就會在作用域鏈中尋找具有相應名字的變量。函數執行完畢,活動對象就被銷毀。而閉包的特殊就在於此,活動對象沒有被銷毀!

 

看這段代碼:

function compare(propertyValue){
    return function(obj1,obj2){
        var a=obj1;
        var b=obj2;
        if(a<b){
            return true;
        }else if(a>b){
            return false;
        }else{
            return 0;
        }
    }
}
var fun=compare("value");
console.log(typeof fun);//function
var result=fun(5,10);
console.log(result);//true

 

函數compare()中包含了一個匿名函數,那么,該匿名函數,也就是閉包的作用域鏈中,就會有compare()函數的活動對象。因此,該閉包作用域鏈其實有三節:

第一節,閉包的活動對象,obj1,obj2 和arguments;

第二節,compare()函數的活動對象,arguments和propertyValue;

第三節,全局變量對象,compare。

 

這意味着什么意思呢?就是在compare()這個函數在執行完畢以后,他的活動對象propertyValue和arguments也不會被銷毀。因為匿名函數的作用域鏈仍然在引用這個活動對象。也就是說,compare()執行完以后,其執行環境的作用域鏈會被銷毀,但是他的活動對象卻不會被銷毀。除非匿名函數被銷毀。

 

而在js中,內置的工具函數setTimeout,往往就會造就一個閉包。

function wait(message){
    setTimeout(function timer(){
        console.log(message);
    },1000);
}
wait('5');//5

 

timer()函數就是一個閉包,即使wait()執行1000毫秒后,內部作用域仍然不會消失。timer具有wait()作用域的閉包。

 

總結:由於閉包會攜帶外部函數的作用域,所以它會占用更多的內存。建議不要過多使用閉包,會導致內存占用過多。

 

三、閉包與變量

function fun(){
    var result=new Array();

    for(var i=0;i<4;i++){
        result[i]=function(){
            return i;
        };
    }
    return result;
}
var a=fun();
console.log(a);//Array(4) [, , , ]

 好吧,這段代碼我還是看的半懂不懂。似乎理解他為什么會返回這個,似乎又不理解。而且和書上寫的也不一樣啊。也沒返回4個“3”啊!這是為啥內?

 

 

for(var i=0;i<2;i++){
    setTimeout(function timer(){
        console.log(i);
    },i*1000);
}
console.log('daoda')//daoda;2;2

 

看到沒有,竟然先輸出了"daoda",然后又輸出了兩個“2”!

首先解釋“2”是怎么來的。首先,這個循環終止的條件是i不再小於2,也就是說,條件首次成立時,i的值為2。因此,輸出顯示的是循環結束時i的值。

延遲函數的回調是在循環結束的時候才執行。即使執行的是setTimeout(...,0),所有的回調函數依然是在循環結束后才會執行。因此每次輸出的都是2!

你以為每次迭代都會有對應的i,但是根據作用域的原理,盡管循環中的5個函數是在各個迭代部分中分別定義的,但是它們卻都被封閉在一個共享的全局作用域中,因此實際上只有1個i。

 

 

for(var i=0;i<2;i++){
    (function(){
        setTimeout(function timer(){
            console.log(i);
        },i*1000);
    })();
}
console.log('藍色橙汁');//"藍色橙汁";2;2

 

 

 

IIFE會立即執行函數,為什么輸出的還是兩個“2”呢?

這是因為IIFE的作用域是空的。他要有自己的變量,用來在每次迭代中存儲i的值才行。

代碼寫成這樣才可以:

for(var i=1;i<=5;i++){
    (function(){
        var j=i;
        setTimeout(function timer(){
            console.log(j);//1,2,3,4,5!這樣就可以了
        },j*1000);
    })();
}

 

上面這段代碼可以做一些改進:

for(var i=1;i<=5;i++){
    (function(j){        
        setTimeout(function timer(){
            console.log(j);//1,2,3,4,5!這樣就可以了
        },j*1000);
    })(i);
}

 

 

 

奇怪的是,為什么這里我就理解了?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM