熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> JSP教程 >> 正文

基於javascript 閉包基礎分享

2013-11-15 12:02:59  來源: JSP教程 

  如果對作用域函數為獨立的對象這樣的基本概念理解較好的話理解閉包的概念並在實際的編程實踐中應用則頗有水到渠成之感

在DOM的事件處理方面大多數程序員甚至自己已經在使用閉包了而不自知在這種情況下對於浏覽器中內嵌的JavaScript引擎的bug可能造成內存洩漏這一問題姑且不論就是程序員自己調試也常常會一頭霧水
用 簡單的語句來描述JavaScript中的閉包的概念由於JavaScript中函數是對象對象是屬性的集合而屬性的值又可以是對象則在函數內 定義函數成為理所當然如果在函數func內部聲明函數inner然後在函數外部調用inner這個過程即產生了一個閉包  
閉包的特性
我們先來看一個例子如果不了解JavaScript的特性很難找到原因

復制代碼 代碼如下:
var outter = [];
        function clouseTest() {
            var array = ["one" "two" "three" "four"];
            for (var i = ; i < arraylength; i++) {
                var x = {};
                xno = i;
                xtext = array[i];
                xinvoke = function () {
                    print(i);
                }
                outterpush(x);
            }
        }
        //調用這個函數
        clouseTest();
        print(outter[]invoke());
        print(outter[]invoke());
        print(outter[]invoke());
        print(outter[]invoke());

  
運行的結果如何呢?很多初學者可能會得出這樣的答案




然而運行這個程序得到的結果為




其 實在每次迭代的時候這樣的語句xinvoke = function(){print(i);}並沒有被執行只是構建了一個函數體為”print(i);”的函數對象如此而已而當i=迭代停 止外部函數返回當再去調用outter[]invoke()時i的值依舊為因此outter數組中的每一個元素的invoke都返回i的 值如何解決這一問題呢?我們可以聲明一個匿名函數並立即執行它

復制代碼 代碼如下:
var outter = [];
        function clouseTest() {
            var array = ["one" "two" "three" "four"];
            for (var i = ; i < arraylength; i++) {
                var x = {};
                xno = i;
                xtext = array[i];
                xinvoke = function (no) {
                    return function () {
                        print(no);
                    }
                }(i);
                outterpush(x);
            }
        }
        clouseTest();
    </script>

  
這個例子中我們為xinvoke賦值的時候先運行一個可以返回一個函數的函數然後立即執行之這樣xinvoke的每一次迭代器時相當與執行這樣的語句

復制代碼 代碼如下:
//x ==
xinvoke = function(){print();}
//x ==
xinvoke = function(){print();}
//x ==
xinvoke = function(){print();}
//x ==
xinvoke = function(){print();}

  
這樣就可以得到正確結果了閉包允許你引用存在於外部函數中的變量然而它並不是使用該變量創建時的值相反它使用外部函數中該變量最後的值
閉包的用途
現在閉包的概念已經清晰了我們來看看閉包的用途事實上通過使用閉包我們可以做很多事情比如模擬面向對象的代碼風格更優雅更簡潔的表達出代碼在某些方面提升代碼的執行效率

緩存
再來看一個例子設想我們有一個處理過程很耗時的函數對象每次調用都會花費很長時間那麼我們就需要將計算出來的值存儲起來當調用這個函數的時候首先在緩存中查找如果找不到則進行計算然後更新緩存並返回值如果找到了直接返回查找到的值即可
閉包正是可以做到這一點因為它不會釋放外部的引用從而函數內部的值可以得以保留

復制代碼 代碼如下:
var CachedSearchBox = (function () {
            var cache = {}
               count = [];
            return {
                attachSearchBox: function (dsid) {
                    if (dsid in cache) {//如果結果在緩存中
                        return cache[dsid];//直接返回緩存中的對象
                    }
                    var fsb = documentgetElementById(dsid);//新建
                    cache[dsid] = fsb;//更新緩存
                    if (countlength > ) {//保正緩存的大小<=
                        delete cache[countshift()];
                    }
                    return fsb;
                }
                clearSearchBox: function (dsid) {
                    if (dsid in cache) {
                        cache[dsid]clearSelection();
                    }
                }
            };
        })();
        var obj = CachedSearchBoxattachSearchBox("input");
        //alert(obj);
        var obj = CachedSearchBoxattachSearchBox("input");

  
實現封裝

復制代碼 代碼如下:
var person = function(){
    //變量作用域為函數內部外部無法訪問
    var name = "default";  

    return {
       getName : function(){
           return name;
       }
       setName : function(newName){
           name = newName;
       }
    }
}();

print(personname);//直接訪問結果為undefined
print(persongetName());
personsetName("jack");
print(persongetName());

  
得到結果如下
undefined
default
jack

  閉包的另一個重要用途是實現面向對象中的對象傳統的對象語言都提供類的模板機制這樣不同的對象(類的實例)擁有獨立的成員及狀態互不干涉雖然JavaScript中沒有類這樣的機制但是通過使用閉包我們可以模擬出這樣的機制還是以上邊的例子來講

復制代碼 代碼如下:
function Person(){
    var name = "default";  

    return {
       getName : function(){
           return name;
       }
       setName : function(newName){
           name = newName;
       }
    }
};
var john = Person();
print(johngetName());
johnsetName("john");
print(johngetName());

var jack = Person();
print(jackgetName());
jacksetName("jack");
print(jackgetName());

  
運行結果如下
default
john
default
jack

  javascript閉包應該注意的問題
內存洩漏
在 不同的JavaScript解釋器實現中由於解釋器本身的缺陷使用閉包可能造成內存洩漏內存洩漏是比較嚴重的問題會嚴重影響浏覽器的響應速度降 低用戶體驗甚至會造成浏覽器無響應等現象JavaScript的解釋器都具備垃圾回收機制一般采用的是引用計數的形式如果一個對象的引用計數為 零則垃圾回收機制會將其回收這個過程是自動的但是有了閉包的概念之後這個過程就變得復雜起來了在閉包中因為局部的變量可能在將來的某些時刻 需要被使用因此垃圾回收機制不會處理這些被外部引用到的局部變量而如果出現循環引用即對象A引用BB引用C而C又引用到A這樣的情況使得垃圾 回收機制得出其引用計數不為零的結論從而造成內存洩漏

上下文的引用

復制代碼 代碼如下:
$(function(){
    var con = $("div#panel");
    thisid = "content";
    conclick(function(){
       alert(thisid);//panel
    });
});

  
此處的alert(thisid)到底引用著什麼值呢?很多開發者可能會根據閉包的概念做出錯誤的判斷
content
理 由是thisid顯示的被賦值為content而在click回調中形成的閉包會引用到thisid因此返回值為content然而事實 上這個alert會彈出”panel”究其原因就是此處的this雖然閉包可以引用局部變量但是涉及到this的時候情況就有些微妙了因為 調用對象的存在使得當閉包被調用時(當這個panel的click事件發生時)此處的this引用的是con這個jQuery對象而匿名函數中的 thisid = “content”是對匿名函數本身做的操作兩個this引用的並非同一個對象
如果想要在事件處理函數中訪問這個值我們必須做一些改變

復制代碼 代碼如下:
$(function(){
    var con = $("div#panel");
    thisid = "content";
    var self = this;
    conclick(function(){
       alert(selfid);//content
    });
});

  
這樣我們在事件處理函數中保存的是外部的一個局部變量self的引用而並非this這種技巧在實際應用中多有應用我們在後邊的章節裡進行詳細討論關於閉包的更多內容我們將在第九章詳細討論包括討論其他命令式語言中的“閉包”閉包在實際項目中的應用等等
由於本身水平有限文中難免有纰漏錯誤等或者語言本身有不妥當之處歡迎及時指正提出建議本文只為拋磚引玉謝謝大家!


From:http://tw.wingwit.com/Article/program/Java/JSP/201311/20159.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.