【javascript】閉包

閉包

  • 閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)排作。
  • 創(chuàng)建閉包的常見方式米母,就是在一個函數(shù)內(nèi)部創(chuàng)建另一個函數(shù)
function createComparisonFunction(propertyName) {
    return function(object1, object2){
    //內(nèi)部函數(shù)的作用域鏈中包含createComparisonFunction()的作用域
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        if (value1 < value2){
            return -1;
        } else if (value1 > value2){
            return 1;
        } else {
            return 0;
        }
    };
}
  • 當(dāng)某個函數(shù)被調(diào)用時矫钓,會創(chuàng)建一個執(zhí)行環(huán)境及相應(yīng)的作用域鏈要尔。然后舍杜,使用arguments 和其他命名參數(shù)的值來初始化函數(shù)的活動對象但在作用域鏈中,外部函數(shù)的活動對象始終處于第二位赵辕,外部函數(shù)的外部函數(shù)的活動對象處于第三位既绩,……直至作為作用域鏈終點(diǎn)的全局執(zhí)行環(huán)境。

  • 在另一個函數(shù)內(nèi)部定義的函數(shù)會將包含函數(shù)(即外部函數(shù))的活動對象添加到它的作用域鏈中还惠。

//創(chuàng)建函數(shù)
var compareNames = createComparisonFunction("name");
//調(diào)用函數(shù)
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
//解除對匿名函數(shù)的引用(以便釋放內(nèi)存)
compareNames = null;
  • 閉包會在父函數(shù)外部饲握,改變父函數(shù)內(nèi)部變量的值。所以蚕键,如果你把父函數(shù)當(dāng)作對象(object)使用救欧,把閉包當(dāng)作它的公用方法(PublicMethod),把內(nèi)部變量當(dāng)作它的私有屬性(private value)锣光,這時一定要小心笆怠,不要隨便改變父函數(shù)內(nèi)部變量的值。

閉包與變量

  • 作用域鏈的這種配置機(jī)制引出了一個值得注意的副作用誊爹,即閉包只能取得包含函數(shù)中任何變量的最后一個值蹬刷。
function createFunctions(){
    var result = new Array();
    for (var i=0; i < 10; i++){
        result[i] = function(){
            return i;
        };
    }
    return result;
}
//這個函數(shù)會返回一個函數(shù)數(shù)組,每個函數(shù)都返回10
/**因為每個函數(shù)的作用域鏈中都保存著createFunctions()函數(shù)的活動對象,所以它們引用的都是同一個變量i 频丘。當(dāng)createFunctions()函數(shù)返回后办成,變量i的值是10,此時每個函數(shù)都引用著保存變量i 的同一個變量對象搂漠,所以在每個函數(shù)內(nèi)部i 的值都是10**/
  • 我們可以通過創(chuàng)建另一個匿名函數(shù)強(qiáng)制讓閉包的行為符合預(yù)期
function createFunctions(){
    var result = new Array();
    for (var i=0; i < 10; i++){
        result[i] = function(num){
            //創(chuàng)建并返回了一個訪問num 的閉包
            return function(){
                return num;
            };
        }(i);//將立即執(zhí)行改匿名函數(shù)的結(jié)果賦給數(shù)組
    }
    return result;
}

關(guān)于this對象

  • this 對象是在運(yùn)行時基于函數(shù)的執(zhí)行環(huán)境綁定的:在全局函數(shù)中迂卢,this等于window,而當(dāng)函數(shù)被作為某個對象的方法調(diào)用時桐汤,this 等于那個對象
  • 匿名函數(shù)的執(zhí)行環(huán)境具有全局性而克,因此其this對象通常指向window(在通過call()或apply()改變函數(shù)執(zhí)行環(huán)境的情況下,this 就會指向其他對象惊科。)
  • 但有時候由于編寫閉包的方式不同,這一點(diǎn)可能不會那么明顯.
var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        return function(){
            return this.name;
        };
    }
};
alert(object.getNameFunc()()); //"The Window"(在非嚴(yán)格模式下)
  • 為什么匿名函數(shù)沒有取得其包含作用域(或外部作用域)的this 對象呢亮钦?
  • 每個函數(shù)在被調(diào)用時都會自動取得兩個特殊變量:this和arguments馆截。內(nèi)部函數(shù)在搜索這兩個變量時,只會搜索到其活動對象為止蜂莉,因此永遠(yuǎn)不可能直接訪問外部函數(shù)中的這兩個變量蜡娶。
  • 把外部作用域中的this對象保存在一個閉包能夠訪問到的變量里,就可以讓閉包訪問該對象了.
var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        var that = this;
        return function(){
            return that.name;
        };
    }
};
alert(object.getNameFunc()()); //"My Object"
/**在定義匿名函數(shù)之前映穗,我們把this對象賦值給了一個名叫that的變量窖张。而在定義了閉包之后,閉包也可以訪問這個變量蚁滋,因為它是我們在包含函數(shù)中特意聲名的一個變量宿接。即使在函數(shù)返回之后赘淮,that 也仍然引用著object,所以調(diào)用object.getNameFunc()()就返回了"My Object"睦霎。**/

內(nèi)存泄露

  • 由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中梢卸,內(nèi)存消耗很大,所以不能濫用閉包副女,否則會造成網(wǎng)頁的性能問題蛤高,在IE中可能導(dǎo)致內(nèi)存泄露。
  • 如果閉包的作用域鏈中保存著一個HTML 元素碑幅,那么就意味著該元素將無法被銷毀.
function assignHandler(){
    var element = document.getElementById("someElement");
    element.onclick = function(){
        alert(element.id);
    };
}
/**由于匿名函數(shù)保存了一個對assignHandler()的活動對象的引用戴陡,因此就會導(dǎo)致無法減少element 的引用數(shù)。只要匿名函數(shù)存在沟涨,element的引用數(shù)至少也是1恤批,因此它所占用的內(nèi)存就永遠(yuǎn)不會被回收**/
  • 解決方法是,在退出函數(shù)之前拷窜,將不使用的局部變量全部刪除开皿。
function assignHandler(){
    var element = document.getElementById("someElement");
    var id = element.id;
    element.onclick = function(){
        alert(id);
    };
    element = null;
}

模仿塊級作用域

  • javaScript 沒有塊級作用域的概念,意味著在塊語句中定義的變量篮昧,實(shí)際上是在包含
    函數(shù)中而非語句中創(chuàng)建的
function outputNumbers(count){
    for (var i=0; i < count; i++){
        alert(i);
    }
    alert(i); //計數(shù)
}
//即使像下面這樣錯誤地重新聲明同一個變量赋荆,也不會改變它的值。
function outputNumbers(count){
    for (var i=0; i < count; i++){
        alert(i);
    }
    var i; //重新聲明變量
    alert(i); //計數(shù)
}
  • 匿名函數(shù)可以用來模仿塊級作用域并避免這個問題懊昨。
//用作塊級作用域(通常稱為私有作用域)的匿名函數(shù)的語法
(function(){
//這里是塊級作用域
})();
  • 這種技術(shù)經(jīng)常在全局作用域中被用在函數(shù)外部窄潭,從而限制向全局作用域中添加過多的變量和函數(shù)。
(function(){
    var now = new Date();
    if (now.getMonth() == 0 && now.getDate() == 1){
        alert("Happy new year!");
    }
})();

/**這種做法可以減少閉包占用的內(nèi)存問題酵颁,因為沒有指向匿名函數(shù)的引用嫉你。只要函
數(shù)執(zhí)行完畢,就可以立即銷毀其作用域鏈了躏惋。**/
好好學(xué)習(xí)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末幽污,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子簿姨,更是在濱河造成了極大的恐慌距误,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,331評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扁位,死亡現(xiàn)場離奇詭異准潭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)域仇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評論 3 398
  • 文/潘曉璐 我一進(jìn)店門刑然,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人暇务,你說我怎么就攤上這事泼掠≌恚” “怎么了?”我有些...
    開封第一講書人閱讀 167,755評論 0 360
  • 文/不壞的土叔 我叫張陵武鲁,是天一觀的道長爽雄。 經(jīng)常有香客問我,道長沐鼠,這世上最難降的妖魔是什么挚瘟? 我笑而不...
    開封第一講書人閱讀 59,528評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮饲梭,結(jié)果婚禮上乘盖,老公的妹妹穿的比我還像新娘。我一直安慰自己憔涉,他們只是感情好订框,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,526評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著兜叨,像睡著了一般穿扳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上国旷,一...
    開封第一講書人閱讀 52,166評論 1 308
  • 那天矛物,我揣著相機(jī)與錄音,去河邊找鬼跪但。 笑死履羞,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的屡久。 我是一名探鬼主播忆首,決...
    沈念sama閱讀 40,768評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼被环!你這毒婦竟也來了糙及?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,664評論 0 276
  • 序言:老撾萬榮一對情侶失蹤筛欢,失蹤者是張志新(化名)和其女友劉穎浸锨,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悴能,經(jīng)...
    沈念sama閱讀 46,205評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡揣钦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,290評論 3 340
  • 正文 我和宋清朗相戀三年雳灾,在試婚紗的時候發(fā)現(xiàn)自己被綠了漠酿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,435評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡谎亩,死狀恐怖炒嘲,靈堂內(nèi)的尸體忽然破棺而出宇姚,到底是詐尸還是另有隱情,我是刑警寧澤夫凸,帶...
    沈念sama閱讀 36,126評論 5 349
  • 正文 年R本政府宣布浑劳,位于F島的核電站,受9級特大地震影響夭拌,放射性物質(zhì)發(fā)生泄漏魔熏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,804評論 3 333
  • 文/蒙蒙 一鸽扁、第九天 我趴在偏房一處隱蔽的房頂上張望蒜绽。 院中可真熱鬧,春花似錦桶现、人聲如沸躲雅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,276評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽相赁。三九已至,卻和暖如春慰于,著一層夾襖步出監(jiān)牢的瞬間钮科,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工东囚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跺嗽,地道東北人。 一個月前我還...
    沈念sama閱讀 48,818評論 3 376
  • 正文 我出身青樓页藻,卻偏偏與公主長得像桨嫁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子份帐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,442評論 2 359

推薦閱讀更多精彩內(nèi)容