javascript閉包

前言

之前對閉包的理解一直不是很清晰丝蹭,總覺得自己已經(jīng)理解了,面試的時(shí)候也說得頭頭是道苦掘,但是看了看了一篇有關(guān)javascript閉包內(nèi)存泄漏的文章后發(fā)現(xiàn)自己對閉包的理解完全不夠

一、什么是閉包

閉包是捆綁在一起(封閉)的函數(shù)與對其周圍狀態(tài)(詞法作用域環(huán)境下)的引用的組合。 換句話說,閉包使你能從內(nèi)部函數(shù)訪問外部函數(shù)的作用域。 在JavaScript中,每次創(chuàng)建函數(shù)時(shí)腾誉,都會(huì)在函數(shù)創(chuàng)建時(shí)創(chuàng)建閉包。

//創(chuàng)建閉包的一般示例
function a() {
  var b = 1;
  function c() {
    console.log(b);
  }
}
function a() {
  var b = 1;
  return function c() {
    console.log(b);
  }
}
function a(b) {
  function c() {
    console.log(b);
  }
}
//直接在全局環(huán)境創(chuàng)建函數(shù)實(shí)際上也是一個(gè)閉包,因?yàn)闈M足閉包的定義
var a = 1;
function b() {
  console.log(a);
}

二菲语、如何使用閉包

要使用閉包妄辩,只需在另一個(gè)函數(shù)內(nèi)定義一個(gè)函數(shù)并公開它。 公開函數(shù)的方式有兩種山上,一種是直接return嵌套函數(shù)眼耀,另一種是將嵌套函數(shù)作為參數(shù)傳遞給另外一個(gè)函數(shù)

//直接return嵌套函數(shù)的方式一
var accept;
function a() {
  var b = 1;
  return function c() {
    console.log(b);
  }
}
accept = a();
//直接return嵌套函數(shù)的方式二
var accept;
function a() {
  var b = 1;
  function c() {
    console.log(b);
  }
  accept = c;
}
//作為參數(shù)傳遞的方式
function accept(func) {
  ...
}
function a() {
  var b = 1;
  function c() {
    console.log(b);
  }
  accept(c);
}

即便我們暴露出了內(nèi)部函數(shù)(如上文中的函數(shù)c),并且外部函數(shù)(如上文中的a函數(shù))已經(jīng)執(zhí)行結(jié)束并返回佩憾,內(nèi)部函數(shù)仍然可以訪問外部函數(shù)的作用域

還有一種比較特殊的閉包哮伟,立即執(zhí)行函數(shù),通過構(gòu)建函數(shù)作用域的形式將變量與全局命名空間隔離妄帘, 并通過閉包的形式讓它們存在于整個(gè)運(yùn)行時(shí)楞黄,一般用于保護(hù)全局命名空間免受變量污染

(function(window){
    var foo, bar;
    function private(){
        // do something
    }
    window.Module = {
        public: function(){
            // do something 
        }
    };
})(this);

三、閉包的作用

//在函數(shù)a執(zhí)行完后抡驼,i不會(huì)被回收鬼廓,它會(huì)存在于b的作用域鏈上
function a() {
    var i = 0;
    function b() {
        console.log(++i);
    }
    return b;
}
var c = a();
c();    //1
c();    //2

其實(shí)可以理解為b對a的context有訪問權(quán)限,換句話說致盟,就是b在自身的活動(dòng)對象找不到的情況下碎税,會(huì)在作用域鏈上去找

四、閉包的規(guī)則

一個(gè)函數(shù)a里的所有閉包函數(shù)(內(nèi)嵌函數(shù))都共享同一個(gè)context馏锡,這個(gè)context來源于函數(shù)a雷蹂。

當(dāng)函數(shù)a執(zhí)行完畢后,所有沒有被閉包函數(shù)引用的變量都會(huì)被垃圾回收杯道,而所有被閉包函數(shù)引用的變量都會(huì)作為context匪煌。context被所有閉包函數(shù)共享(即可以通過作用域鏈訪問到)。若存在一個(gè)閉包函數(shù)被函數(shù)a暴露給外部引用党巾,那么函數(shù)a執(zhí)行完后萎庭,context中的變量不會(huì)被垃圾回收

function PING() {
    this.a = new Array(1000000);
}
function a() {
    var ping = new PING();
    var obj = {name: 'carl', arr: new Array(1000000)};
    var obj2 = {name: 'jack', arr: new Array(1000000)};
    function f1() {
        if(obj) {}
    }
    function f2() {
        if(ping) {}
    }
    return function b() {}
}
var c = a();
執(zhí)行結(jié)果.png

如果函數(shù)a里使用了eval且把a中的內(nèi)嵌函數(shù)b暴露給外部引用的話,那么a中所有的b除外的變量都不會(huì)被回收

function PING() {
    this.a = new Array(1000000);
}
function a() {
    var ping = new PING();
    var obj = {name: 'carl', arr: new Array(1000000)};
    var obj2 = {name: 'jack', arr: new Array(1000000)};
    function f() { eval(); }
    return function b() {}
}
var c = a();
執(zhí)行結(jié)果.png

五齿拂、看一下有關(guān)閉包的內(nèi)存泄漏的代碼

var theThing = null;
var replaceThing = function () {
    var originalThing = theThing;   
    var unused = function () {
        if(originalThing) {}
    };
    theThing = {
        longStr: Date.now() + Array(1000000).join('*'),
        someMethod: function () {}
    };
};
setInterval(replaceThing, 1000);

分析
第一秒:someMethod 被暴露到外部驳规,unused引用了originalThingsomeMethod的context一直包含著originalThing创肥,故originalThing沒被回收,originalThing指向null

第二秒:someMethod 被暴露到外部,unused引用了originalThing叹侄,someMethod的context一直包含著originalThing巩搏,故originalThing沒被回收,但是originalThing指向了第一秒的theThing所指向的內(nèi)存空間(包含第一秒的someMethod趾代,所以第一秒的originalThing到了第二秒還是沒有被回收贯底,但是originalThing指向null,所以問題不大)

第三秒:someMethod 被暴露到外部撒强,unused引用了originalThing禽捆,someMethod的context一直包含著originalThing,故originalThing沒被回收飘哨,但是originalThing指向了第二秒的theThing所指向的內(nèi)存空間(包含第二秒的someMethod胚想,所以第二秒的originalThing到了第三秒還是沒有被回收,但是originalThing指向第二秒的theThing所指向的內(nèi)存空間芽隆,這個(gè)空間很大浊服,問題也很大)

第四秒:...

綜上所述,第三秒沒有回收第二秒的theThing指向的內(nèi)存空間胚吁,第四秒也沒回收第二秒的theThing指向的內(nèi)存空間和第三秒的theThing指向的內(nèi)存空間...

最后牙躺,上面這種閉包的寫法是不合理的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市腕扶,隨后出現(xiàn)的幾起案子孽拷,更是在濱河造成了極大的恐慌,老刑警劉巖半抱,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脓恕,死亡現(xiàn)場離奇詭異,居然都是意外死亡代虾,警方通過查閱死者的電腦和手機(jī)进肯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棉磨,“玉大人江掩,你說我怎么就攤上這事〕巳浚” “怎么了环形?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長衙傀。 經(jīng)常有香客問我抬吟,道長,這世上最難降的妖魔是什么统抬? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任火本,我火速辦了婚禮危队,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钙畔。我一直安慰自己茫陆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布擎析。 她就那樣靜靜地躺著簿盅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪揍魂。 梳的紋絲不亂的頭發(fā)上桨醋,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音现斋,去河邊找鬼喜最。 笑死,一個(gè)胖子當(dāng)著我的面吹牛步责,可吹牛的內(nèi)容都是我干的返顺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蔓肯,長吁一口氣:“原來是場噩夢啊……” “哼遂鹊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蔗包,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤秉扑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后调限,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舟陆,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年耻矮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秦躯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡裆装,死狀恐怖踱承,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哨免,我是刑警寧澤茎活,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站琢唾,受9級(jí)特大地震影響载荔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜采桃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一懒熙、第九天 我趴在偏房一處隱蔽的房頂上張望丘损。 院中可真熱鬧,春花似錦工扎、人聲如沸号俐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至踪危,卻和暖如春蔬浙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贞远。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工畴博, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蓝仲。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓俱病,卻偏偏與公主長得像,于是被迫代替她去往敵國和親袱结。 傳聞我的和親對象是個(gè)殘疾皇子亮隙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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