亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

基于javascript 閉包基礎分享

2019-11-20 22:32:33
字體:
來源:轉載
供稿:網友

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

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

復制代碼 代碼如下:

var outter = [];
        function clouseTest() {
            var array = ["one", "two", "three", "four"];
            for (var i = 0; i < array.length; i++) {
                var x = {};
                x.no = i;
                x.text = array[i];
                x.invoke = function () {
                    print(i);
                }
                outter.push(x);
            }
        }
        //調用這個函數
        clouseTest();
        print(outter[0].invoke());
        print(outter[1].invoke());
        print(outter[2].invoke());
        print(outter[3].invoke());

運行的結果如何呢?很多初學者可能會得出這樣的答案:
0
1
2
3
然而,運行這個程序,得到的結果為:
4
4
4
4
其實,在每次迭代的時候,這樣的語句x.invoke = function(){print(i);}并沒有被執行,只是構建了一個函數體為”print(i);”的函數對象,如此而已。而當i=4時,迭代停止,外部函數返回,當再去調用outter[0].invoke()時,i的值依舊為4,因此outter數組中的每一個元素的invoke都返回i的值:4。如何解決這一問題呢?我們可以聲明一個匿名函數,并立即執行它:
復制代碼 代碼如下:

var outter = [];
        function clouseTest2() {
            var array = ["one", "two", "three", "four"];
            for (var i = 0; i < array.length; i++) {
                var x = {};
                x.no = i;
                x.text = array[i];
                x.invoke = function (no) {
                    return function () {
                        print(no);
                    }
                }(i);
                outter.push(x);
            }
        }
        clouseTest2();
    </script>

這個例子中,我們為x.invoke賦值的時候,先運行一個可以返回一個函數的函數,然后立即執行之,這樣,x.invoke的每一次迭代器時相當與執行這樣的語句:
復制代碼 代碼如下:

//x == 0
x.invoke = function(){print(0);}
//x == 1
x.invoke = function(){print(1);}
//x == 2
x.invoke = function(){print(2);}
//x == 3
x.invoke = function(){print(3);}

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

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

var CachedSearchBox = (function () {
            var cache = {},
               count = [];
            return {
                attachSearchBox: function (dsid) {
                    if (dsid in cache) {//如果結果在緩存中
                        return cache[dsid];//直接返回緩存中的對象
                    }
                    var fsb = document.getElementById(dsid);//新建
                    cache[dsid] = fsb;//更新緩存
                    if (count.length > 100) {//保正緩存的大小<=100
                        delete cache[count.shift()];
                    }
                    return fsb;
                },
                clearSearchBox: function (dsid) {
                    if (dsid in cache) {
                        cache[dsid].clearSelection();
                    }
                }
            };
        })();
        var obj1 = CachedSearchBox.attachSearchBox("input1");
        //alert(obj1);
        var obj2 = CachedSearchBox.attachSearchBox("input1");

實現封裝:
復制代碼 代碼如下:

var person = function(){
    //變量作用域為函數內部,外部無法訪問
    var name = "default";  

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

print(person.name);//直接訪問,結果為undefined
print(person.getName());
person.setName("jack");
print(person.getName());

得到結果如下:
undefined
default
jack

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

復制代碼 代碼如下:

function Person(){
    var name = "default";  

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

var jack = Person();
print(jack.getName());
jack.setName("jack");
print(jack.getName());

運行結果如下:
default
john
default
jack

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

2.上下文的引用:

復制代碼 代碼如下:

$(function(){
    var con = $("div#panel");
    this.id = "content";
    con.click(function(){
       alert(this.id);//panel
    });
});

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

$(function(){
    var con = $("div#panel");
    this.id = "content";
    var self = this;
    con.click(function(){
       alert(self.id);//content
    });
});

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲美女精品成人在线视频| 欧美二区乱c黑人| 亚洲人成在线免费观看| 日韩成人在线视频网站| 欧美风情在线观看| 欧美午夜美女看片| 性亚洲最疯狂xxxx高清| 亚洲毛片在线免费观看| 亚洲热线99精品视频| 国产精品成人av在线| 国产精品av网站| 亚洲欧美日韩图片| 亚洲片av在线| 午夜剧场成人观在线视频免费观看| 亚洲综合在线播放| 日韩精品极品在线观看播放免费视频| 日韩欧美福利视频| 国产亚洲欧洲高清一区| 欧美激情精品久久久久久久变态| 国产欧美一区二区三区久久人妖| 久久精品91久久久久久再现| 久久色精品视频| 亚洲美女在线视频| 久久成人综合视频| 久久99精品久久久久久噜噜| 国产亚洲精品高潮| 欧美精品videos| 精品国产精品自拍| 欧美一级电影久久| 国产精品欧美一区二区| 欧美电影免费在线观看| 最近中文字幕mv在线一区二区三区四区| 日本久久久a级免费| 国产精品视频成人| 国产成人精品网站| 色偷偷偷亚洲综合网另类| 久久久免费观看| 国产精品xxxxx| 亚洲激情视频在线观看| 日韩小视频网址| 日本精品在线视频| 欧美亚洲免费电影| 精品国产依人香蕉在线精品| 久久精品电影网站| 亚洲第一视频网| 久久久久久久久综合| 亚洲欧美在线播放| 国产成人精品一区二区三区| 国产精品自拍偷拍视频| 亚洲男人天堂久| 91牛牛免费视频| 久久久久久九九九| 久久精品久久久久电影| 久久精品国亚洲| 欧美xxxx做受欧美.88| 欧美日韩综合视频网址| 亚洲欧美成人网| 国产成人aa精品一区在线播放| 亚洲第一视频在线观看| 日韩欧美aⅴ综合网站发布| 国产福利精品av综合导导航| 国产精品伦子伦免费视频| 日韩电影免费观看中文字幕| 91精品国产电影| 一夜七次郎国产精品亚洲| 91久久久久久久一区二区| 亚洲国产精久久久久久| 色偷偷偷亚洲综合网另类| 国产精品99一区| www.日韩系列| 粉嫩老牛aⅴ一区二区三区| 美女啪啪无遮挡免费久久网站| 国产成人精彩在线视频九色| 国产精品爱啪在线线免费观看| 日韩中文字幕精品视频| 亚洲成人aaa| 国产日韩欧美黄色| 日韩精品极品视频免费观看| 国产日本欧美视频| 狠狠干狠狠久久| 国产精品一区二区久久久| 欧美激情高清视频| 日韩欧美成人免费视频| 黄色精品在线看| 欧美国产在线视频| 亚洲久久久久久久久久久| 一区二区三区国产视频| 国产精品∨欧美精品v日韩精品| 国产欧美日韩专区发布| 亚洲色图35p| 日韩精品丝袜在线| 国产91精品最新在线播放| 国产91精品在线播放| 亚洲精品短视频| 亚洲精品电影在线观看| 国产精品嫩草视频| 国产成人欧美在线观看| 日韩免费视频在线观看| 亚洲xxxx18| 日韩精品小视频| 国产欧美亚洲视频| 亚洲大胆人体av| 高清欧美性猛交xxxx| 另类图片亚洲另类| 少妇久久久久久| 国产亚洲一级高清| 欧美成aaa人片免费看| 国产成人精品久久二区二区91| 日韩一区二区精品视频| 国产精品高潮呻吟视频| 社区色欧美激情 | 久久久久久中文字幕| 欧美激情久久久久| 亚洲深夜福利在线| 日韩69视频在线观看| 黄色成人av网| 国产一区二区三区高清在线观看| 精品久久久国产精品999| 国内精品小视频在线观看| 欧美激情图片区| 97热在线精品视频在线观看| 久热爱精品视频线路一| 久久久国产精品免费| 久久久午夜视频| 亚洲国产成人久久综合一区| 精品亚洲精品福利线在观看| 日韩在线激情视频| 亚洲欧美变态国产另类| 国内精品中文字幕| 亚洲三级 欧美三级| 国产不卡av在线免费观看| 国产精品久久久久久久天堂| www.日韩av.com| 操人视频在线观看欧美| 日韩在线视频国产| 国产美女精品视频| 亚洲男人第一av网站| 在线播放国产一区二区三区| 在线日韩中文字幕| 日本精品一区二区三区在线| 精品中文字幕在线观看| 久久中文字幕在线视频| 亚洲激情视频在线| 黑人巨大精品欧美一区二区免费| 久久久精品2019中文字幕神马| 欧美在线视频一二三| 亚洲久久久久久久久久久| 国产精品美女久久久免费| 国产精品扒开腿做爽爽爽视频| 久久综合久久88| 欧美国产精品va在线观看| 欧美电影免费观看高清完整| 国产美女被下药99| 免费91麻豆精品国产自产在线观看| 国产精品第10页| 亚洲视频欧洲视频| 日韩中文视频免费在线观看| 欧美日韩激情视频8区| 欧美激情精品在线| 久久精品国产电影| 亚洲女人天堂av| 亚洲欧美三级伦理| 国产精品高潮呻吟久久av黑人| 亚洲色图偷窥自拍|