談談我對JS閉包的理解


這一篇博客承接上一篇,如果大家沒看上一篇,建議看看.....直通車..... 好吧,咱們一起來看看這個閉包,這次我們的重點並不是弄明白閉包是什么?而是搞清楚JS的閉包是怎么產生的。接着上一篇博客的示例:

var a = function(x){
    var b = 'bb';
    var inner = function(){
        var c = 'cc';
    };
    return b;
};

當a函數執行到給inner變量賦值匿名函數之后,形成下面的引用關系,直接復用上次博客的圖:

從上圖很容易看出,這時候inner函數對象的[[Scope]]內部屬性引用着函數a在執行期的活動對象(這里包含了a函數的全部局部變量)和全局作用域。如果這時候我們把這個inner函數傳遞出去,如下代碼:

var a = function(x){
    var b = 'bb';
    var inner = function(){
        var c = 'cc';
    };
    return inner ;
};

var closure = a();

這段代碼意思是:執行a函數,把inner函數返回,並把它賦值給closure變量,這時候全局變量closure和之前的局部變量inner引用同一個函數,大家可以運行下面的代碼:

var a = function(x){
    var b = 'bb';
    var inner = function(){
        var c = 'cc';
        console.log(inner === closure);
    };
    return inner ;
};

var closure = a();

closure();/*輸出控制台:true*/

這段代碼證實了之前所言變量closure和inner引用同一個函數對象,換句話說就是,名為inner的函數和名為closure的函數是同一個函數,closure函數的作用域鏈和inner函數的當然也是一樣一樣的,如下圖:

這時候再結合上一篇博客,大家該明白了,為什么當a函數返回后,closure函數還可以使用a函數內部的局部變量了吧。

這時候還有一個問題,那就是類比其他語言時,例如C語言,當一個函數執行完畢返回后,應該釋放所有這個函數執行期所占用的內存資源,當然也包含它的局部變量了,如果不這么做,不是會造成內存泄露么?JS解釋器當然會釋放,但是JS解釋器是這樣做的:當函數執行完畢后,由JS解釋器的垃圾回收機制來釋放內存,它的工作機制大體上是這樣的:它會定期執行,判斷一塊被占用的內存區域是否還被可用變量所引用,如果沒有,直接釋放內存,如果有,那就不釋放。其實我們在編寫JS代碼時是沒有辦法直接銷毀對象所占用的內存空間的,內存的釋放基本依賴JS的垃圾回收機制。那么有人會說delete運算符,其實delete運算符也無法銷毀對象所占用的內存空間,如下示例:

var a = {k:'占用內存'};
var b = a;

delete a;



console.log(b);/*{k:'占用內存'}對象並未被釋放*/

console.log(a);/*語法錯誤,a is not defined*/

 

那么delete到底做了什么呢?它銷毀了a變量,這樣原來變量a和b都引用{k:'占用內存'}對象所占的內存區域,現在銷毀a后,就只有b引用這塊內存區域了。如果我們再執行下一條運算:

delete b;

這時候,就沒有任何可用變量引用{k:'占用內存'}對象所占的內存區域了,垃圾回收機制自然會識別並釋放資源。

回到閉包的話題上來,如果不產生閉包的話,a函數執行完后。a函數的執行期上下文自然會被垃圾回收機制所識別釋放,a函數內部的的局部變量inner函數對象也會被釋放掉,這點毫無疑問,不會留下任何內存泄露的問題。但是,如果將inner函數對象作為結果返回並賦值給了closure,產生了閉包,那么這個函數對象將不會被銷毀,它的內部變量[[Scope]]引用的作用域鏈當然也就不會被銷毀。一切都這么自然而然的發生了........^_^

不過JS的閉包,如果使用不當,是很容易造成內存泄露的,但是鑒於它的好處,我們既要使用它,也要避免造成內存泄露,所以深刻的理解它很重要^_^

好吧,今天就這些。如有錯誤,請輕拍............

 


免責聲明!

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



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