什么是閉包奠蹬?

當函數(shù)可以記住并訪問所在的詞法作用域時朝聋,就產(chǎn)生了閉包,即使函數(shù)是在當前詞法作用域之外執(zhí)行囤躁。
                                                  —— 你不知道的JavaScript(上卷)

一冀痕、閉包的定義

1、閉包的構(gòu)成
首先狸演,閉包由兩部分構(gòu)成:函數(shù)言蛇、創(chuàng)建該函數(shù)的環(huán)境,環(huán)境由閉包創(chuàng)建時在作用域中的任何局部變量組成宵距。

JS的變量作用域
變量的作用域有兩種:全局變量和局部變量腊尚。
函數(shù)內(nèi)部可以直接讀取全局變量,但是在函數(shù)外部無法讀取函數(shù)內(nèi)部的局部變量满哪。

那么婿斥,如何從外部讀取函數(shù)內(nèi)部的局部變量?在下面的代碼中哨鸭,函數(shù)f2就被包括在函數(shù)f1內(nèi)部民宿,這時f1內(nèi)部的所有局部變量,對f2都是可見的像鸡。但是反過來就不行勘高,f2內(nèi)部的局部變量,對f1就是不可見的。
這就是Javascript語言特有的 鏈式作用域 結(jié)構(gòu)(chain scope):子對象會一級一級地向上尋找所有父對象的變量华望。所以蕊蝗,父對象的所有變量,對子對象都是可見的赖舟,反之則不成立蓬戚。
既然f2可以讀取f1中的局部變量,那么只要把f2作為返回值宾抓,就可以在f1外部讀取f1的內(nèi)部變量子漩。

function f1(){
  var n=999;
  function f2(){
    alert(n); 
  }
}
var test = f1();// 外部函數(shù)調(diào)用之后其變量對象n本應(yīng)該被銷毀
test();// alert 999,但閉包阻止了它們的銷毀石洗,所以仍然可以訪問外部函數(shù)的變量對象

上面代碼中的f2函數(shù)幢泼,就是閉包。
在JS中讲衫,只有函數(shù)內(nèi)部的成員才能讀取局部變量缕棵。所以在本質(zhì)上,閉包是將函數(shù)內(nèi)部和函數(shù)外部連接起來的橋梁涉兽。

二招驴、閉包的特性

在JavaScript中,外部函數(shù)調(diào)用之后其變量對象本應(yīng)該被銷毀枷畏,但閉包阻止了它們的銷毀别厘,所以仍然可以訪問外部函數(shù)的變量對象。

function addCalculator (x) {
    return function (y) {
        return x + y;
    }
}

var add1 = addCalculator(1);
console.log(add1(1)); //2

add1 = null;// 釋放對閉包的引用
console.log(add1(1)); //Uncaught TypeError: add1 is not a function

通常情況下拥诡,函數(shù)的作用域及其所有變量都會在函數(shù)執(zhí)行結(jié)束后被銷毀触趴。
但如果創(chuàng)建了一個閉包的話,這個函數(shù)的作用域就會一直保存到閉包不存在為止渴肉。

閉包的特性一般為以下幾點:
1冗懦、閉包一般為外部函數(shù)嵌套的內(nèi)部函數(shù)、以及創(chuàng)建該內(nèi)部函數(shù)的環(huán)境組成的宾娜;
2批狐、閉包可以引用外部函數(shù)的參數(shù)和變量扇售,即可以訪問外部的環(huán)境前塔;
3、閉包未被釋放回收的情況下承冰,若閉包中引用了外部函數(shù)的參數(shù)和變量华弓,即使外部環(huán)境已經(jīng)被釋放回收,但是外部函數(shù)的參數(shù)和變量依然不會被垃圾回收機制回收困乒。

三寂屏、閉包的作用

1、通過閉包來模擬私有方法
私有方法有利于限制對代碼的訪問,而且可以避免非核心的方法干擾代碼的公共接口迁霎,減少全局污染吱抚。
下面的示例展現(xiàn)了如何使用閉包來定義公共函數(shù),并令其可以訪問私有函數(shù)和變量考廉。這個方式也稱為模塊模式(module pattern)

var calculator = (function(){
    var a = 1;
    function addCalculator(val){
        a += val
    }
    return {
        add1:function() {
            addCalculator(1);
        },
        add2:function() {
            addCalculator(2);
        },
        result:function() {
            return a
        }
    }
})();

console.log(calculator.result());  // 1
calculator.add1();
console.log(calculator.result());  // 2
calculator.add2();
console.log(calculator.result());  // 4

在之前的示例中秘豹,每個閉包都有它自己的詞法環(huán)境。而這次例子中只創(chuàng)建了一個詞法環(huán)境昌粤,為三個函數(shù)所共享:calculator.add1既绕,calculator.add2和 calculator.result。

該共享環(huán)境創(chuàng)建于一個立即執(zhí)行的匿名函數(shù)體內(nèi)涮坐。這個環(huán)境中包含兩個私有項:名為 a的變量和名為 addCalculator的函數(shù)凄贩,這兩項都無法在這個匿名函數(shù)外部直接訪問,必須通過匿名函數(shù)返回的三個公共函數(shù)訪問袱讹。

這三個公共函數(shù)是共享同一個環(huán)境的閉包疲扎,它們都可以訪問 a變量和 addCalculator函數(shù)。

四廓译、閉包的優(yōu)缺點

1评肆、優(yōu)點

  • 保護函數(shù)內(nèi)的變量安全 ,實現(xiàn)封裝非区,防止變量流入其他環(huán)境發(fā)生命名沖突
  • 在內(nèi)存中維持一個變量瓜挽,可以做緩存(但使用多了同時也是一項缺點,消耗內(nèi)存)
  • 匿名立即執(zhí)行函數(shù)可以減少內(nèi)存消耗

2征绸、缺點

  • 由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中久橙,內(nèi)存消耗很大,所以濫用閉包的話會造成網(wǎng)頁的性能問題管怠,在IE(IE9)之前可能導(dǎo)致內(nèi)存泄露淆衷。
    解決方法:在退出函數(shù)之前,將不使用的局部變量全部刪除(例如手動賦值為null)
  • 由于閉包涉及跨域訪問渤弛,所以會導(dǎo)致性能損失祝拯。
    解決方法:可以通過把跨作用域變量存儲在局部變量中,然后直接訪問局部變量她肯,來減輕對執(zhí)行速度的影響佳头。

引申:為何閉包的不當使用會在IE(IE9)之前可能導(dǎo)致內(nèi)存泄漏,即無法回收變量的問題晴氨?
因為IE(IE9)之前的JavaScript引擎使用的垃圾回收算法是引用計數(shù)法康嘉,對于循環(huán)引用將會導(dǎo)致GC(Garbage Collection)無法回收垃圾。
注:在后面的章節(jié)中籽前,我會找機會繼續(xù)討論什么是GC(Garbage Collection)

PS:更新來啦O(∩_∩)O亭珍,下一篇文章講到了GC(Garbage Collection):JS的垃圾回收機制

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敷钾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子肄梨,更是在濱河造成了極大的恐慌阻荒,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件众羡,死亡現(xiàn)場離奇詭異财松,居然都是意外死亡,警方通過查閱死者的電腦和手機纱控,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門辆毡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人甜害,你說我怎么就攤上這事舶掖。” “怎么了尔店?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵眨攘,是天一觀的道長。 經(jīng)常有香客問我嚣州,道長鲫售,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任该肴,我火速辦了婚禮情竹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匀哄。我一直安慰自己秦效,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布涎嚼。 她就那樣靜靜地躺著阱州,像睡著了一般。 火紅的嫁衣襯著肌膚如雪法梯。 梳的紋絲不亂的頭發(fā)上苔货,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音立哑,去河邊找鬼夜惭。 笑死,一個胖子當著我的面吹牛刁憋,可吹牛的內(nèi)容都是我干的滥嘴。 我是一名探鬼主播木蹬,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼至耻,長吁一口氣:“原來是場噩夢啊……” “哼若皱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尘颓,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤走触,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后疤苹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體互广,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年卧土,在試婚紗的時候發(fā)現(xiàn)自己被綠了惫皱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡尤莺,死狀恐怖旅敷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情颤霎,我是刑警寧澤媳谁,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站友酱,受9級特大地震影響晴音,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缔杉,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一锤躁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧或详,春花似錦进苍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至沈贝,卻和暖如春杠人,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宋下。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工嗡善, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人学歧。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓罩引,卻偏偏與公主長得像,于是被迫代替她去往敵國和親枝笨。 傳聞我的和親對象是個殘疾皇子袁铐,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355