什么是閉包仅胞?面試必看!

什么是閉包

什么是閉包,你可能會(huì)搜出很多答案....

《JavaScript高級(jí)程序設(shè)計(jì)》這樣描述:
閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)影兽。

《JavaScript權(quán)威指南》這樣描述:
從技術(shù)的角度講莱革,所有的JavaScript函數(shù)都是閉包:它們都是對(duì)象,它們都關(guān)聯(lián)到作用域鏈盅视。

《你不知道的JavaScript》這樣描述:
當(dāng)函數(shù)可以記住并訪問所在的詞法作用域時(shí)闹击,就產(chǎn)生了閉包,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行。

我最認(rèn)同的是《你不知道的JavaScript》中的描述淆两,雖然前面的兩種說法都沒有錯(cuò)拂酣,但閉包應(yīng)該是基于詞法作用域書寫代碼時(shí)產(chǎn)生的自然結(jié)果,是一種現(xiàn)象剑勾!你也不用為了利用閉包而特意的創(chuàng)建赵颅,因?yàn)殚]包的在你的代碼中隨處可見,只是你還不知道當(dāng)時(shí)你寫的那一段代碼其實(shí)就產(chǎn)生了閉包捂刺。

講解閉包

上面已經(jīng)說到募寨,當(dāng)函數(shù)可以記住并訪問所在的詞法作用域時(shí),就產(chǎn)生了閉包苛谷,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行格郁。

看一段代碼:

function fn1() {
    var name = 'iceman';
    function fn2() {
        console.log(name);
    }
    fn2();
}
fn1();

如果是根據(jù)《JavaScript高級(jí)程序設(shè)計(jì)》和《JavaScript權(quán)威指南》來說独悴,上面的代碼已經(jīng)產(chǎn)生閉包了。fn2訪問到了fn1的變量决采,滿足了條件“有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)”坟奥,fn2本身是個(gè)函數(shù),所以滿足了條件“所有的JavaScript函數(shù)都是閉包”晒喷。

這的確是閉包访敌,但是這種方式定義的閉包不太好觀察。

再看一段代碼:

function fn1() {
    var name = 'iceman';
    function fn2() {
        console.log(name);
    }
    return fn2;
}
var fn3 = fn1();
fn3();

這樣就清晰地展示了閉包:

1.fn2的詞法作用域能訪問fn1的作用域
2.將fn2當(dāng)做一個(gè)值返回
3.fn1執(zhí)行后爷抓,將fn2的引用賦值給fn3
4.執(zhí)行fn3,輸出了變量name

我們知道通過引用的關(guān)系果复,fn3就是fn2函數(shù)本身唉地。執(zhí)行fn3能正常輸出name,這不就是fn2能記住并訪問它所在的詞法作用域耘沼,而且fn2函數(shù)的運(yùn)行還是在當(dāng)前詞法作用域之外了。

正常來說菠隆,當(dāng)fn1函數(shù)執(zhí)行完畢之后狂秘,其作用域是會(huì)被銷毀的,然后垃圾回收器會(huì)釋放那段內(nèi)存空間破衔。而閉包卻很神奇的將fn1的作用域存活了下來钱烟,fn2依然持有該作用域的引用,這個(gè)引用就是閉包读第。

總結(jié):某個(gè)函數(shù)在定義時(shí)的詞法作用域之外的地方被調(diào)用拥刻,閉包可以使該函數(shù)極限訪問定義時(shí)的詞法作用域。

注意:對(duì)函數(shù)值的傳遞可以通過其他的方式吴汪,并不一定值有返回該函數(shù)這一條路蒸眠,比如可以用回調(diào)函數(shù):

function fn1() {
    var name = 'iceman';
    function fn2() {
        console.log(name);
    }
    fn3(fn2);
}
function fn3(fn) {
    fn();
}
fn1();

本例中,將內(nèi)部函數(shù)fn2傳遞給fn3近刘,當(dāng)它在fn3中被運(yùn)行時(shí),它是可以訪問到name變量的介劫。

所以無論通過哪種方式將內(nèi)部的函數(shù)傳遞到所在的詞法作用域以外案淋,它都回持有對(duì)原始作用域的引用,無論在何處執(zhí)行這個(gè)函數(shù)都會(huì)使用閉包踢京。

再次解釋閉包

以上的例子會(huì)讓人覺得有點(diǎn)學(xué)院派了瓣距,但是閉包絕不僅僅是一個(gè)無用的概念,你寫過的代碼當(dāng)中肯定有閉包的身影蹈丸,比如類似如下的代碼:

function waitSomeTime(msg, time) {
    setTimeout(function () {
        console.log(msg)
    }, time);
}
waitSomeTime('hello', 1000);

定時(shí)器中有一個(gè)匿名函數(shù)逻杖,該匿名函數(shù)就有涵蓋waitSomeTime函數(shù)作用域的閉包,因此當(dāng)1秒之后闻伶,該匿名函數(shù)能輸出msg够话。

另一個(gè)很經(jīng)典的例子就是for循環(huán)中使用定時(shí)器延遲打印的問題:

for (var i = 1; i <= 10; i++) {
    setTimeout(function () {
        console.log(i);
    }, 1000);
}

在這段代碼中,我們對(duì)其的預(yù)期是輸出1~10霎箍,但卻輸出10次11澡为。這是因?yàn)閟etTimeout中的匿名函數(shù)執(zhí)行的時(shí)候景埃,for循環(huán)都已經(jīng)結(jié)束了,for循環(huán)結(jié)束的條件是i大于10拒啰,所以當(dāng)然是輸出10次11咯完慧。

究其原因:i是聲明在全局作用中的,定時(shí)器中的匿名函數(shù)也是執(zhí)行在全局作用域中册着,那當(dāng)然是每次都輸出11了。

原因知道了甲捏,解決起來就簡單了司顿,我們可以讓i在每次迭代的時(shí)候,都產(chǎn)生一個(gè)私有的作用域大溜,在這個(gè)私有的作用域中保存當(dāng)前i的值钦奋。

for (var i = 1; i <= 10; i++) {
    (function () {
        var j = i;
        setTimeout(function () {
            console.log(j);
        }, 1000);
    })();
}

這樣就達(dá)到我們的預(yù)期了呀,讓我們用一種比較優(yōu)雅的寫法改造一些疙教,將每次迭代的i作為實(shí)參傳遞給自執(zhí)行函數(shù)伞租,自執(zhí)行函數(shù)中用變量去接收:

for (var i = 1; i <= 10; i++) {
    (function (j) {
        setTimeout(function () {
            console.log(j);
        }, 1000);
    })(i);
}
閉包的應(yīng)用

閉包的應(yīng)用比較典型是定義模塊,我們將操作函數(shù)暴露給外部裸弦,而細(xì)節(jié)隱藏在模塊內(nèi)部:

function module() {
    var arr = [];
    function add(val) {
        if (typeof val == 'number') {
            arr.push(val);
        }
    }
    function get(index) {
        if (index < arr.length) {
            return arr[index]
        } else {
        return null;
        }
    }
    return {
        add: add,
        get: get
    }
}
var mod1 = module();
mod1.add(1);
mod1.add(2);
mod1.add('xxx');
console.log(mod1.get(2));
關(guān)于閉包還有很多要講作喘,這里先講解比較基礎(chǔ)的概念,之后在做詳細(xì)講解窖贤。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赃梧,一起剝皮案震驚了整個(gè)濱河市豌熄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锣险,老刑警劉巖览闰,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焕济,死亡現(xiàn)場離奇詭異盔几,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)上鞠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門芍阎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缨恒,“玉大人,你說我怎么就攤上這事骗露∠麸保” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵叶洞,是天一觀的道長禀崖。 經(jīng)常有香客問我,道長艺晴,這世上最難降的妖魔是什么叶雹? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任折晦,我火速辦了婚禮沾瓦,結(jié)果婚禮上谦炒,老公的妹妹穿的比我還像新娘风喇。我一直安慰自己,他們只是感情好还蹲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布耙考。 她就那樣靜靜地躺著,像睡著了一般斗遏。 火紅的嫁衣襯著肌膚如雪鞋邑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天逾一,我揣著相機(jī)與錄音肮雨,去河邊找鬼酷含。 笑死,一個(gè)胖子當(dāng)著我的面吹牛椅亚,可吹牛的內(nèi)容都是我干的呀舔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼霜瘪,長吁一口氣:“原來是場噩夢啊……” “哼惧磺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起缤底,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎江解,沒想到半個(gè)月后徙歼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡呼股,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年彭谁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了允扇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狭园,死狀恐怖糊治,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绎谦,我是刑警寧澤粥脚,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布刷允,位于F島的核電站,受9級(jí)特大地震影響纤怒,放射性物質(zhì)發(fā)生泄漏天通。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一州既、第九天 我趴在偏房一處隱蔽的房頂上張望萝映。 院中可真熱鬧,春花似錦序臂、人聲如沸蚌卤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逊彭。三九已至,卻和暖如春构订,著一層夾襖步出監(jiān)牢的瞬間侮叮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工悼瘾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留囊榜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓亥宿,卻偏偏與公主長得像卸勺,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子曙求,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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

  • 一堰氓、作用域 A芽淡、定義 代碼在運(yùn)行時(shí),各個(gè)變量豆赏、函數(shù)和對(duì)象的可訪問性挣菲。換句話說,作用域決定了你的代碼里的變量和其他資...
    5吖閱讀 612評(píng)論 0 1
  • 目錄 概述 作用域編譯過程詞法作用域全局作用域函數(shù)作用域 閉包循環(huán)和閉包閉包的用途性能 總結(jié) 概述 作用域和閉包一...
    許驍Charles閱讀 479評(píng)論 0 1
  • [圖片上傳失敗...(image-9ad041-1561218142988)] 目錄 概述 作用域編譯過程詞法作用...
    M1mmm閱讀 274評(píng)論 0 1
  • 提升(var) 我們都認(rèn)為JavaScript代碼執(zhí)行的時(shí)候是由上到下一行一行執(zhí)行的掷邦。但實(shí)際上這并不完全正確白胀,有一...
    hhooke閱讀 1,890評(píng)論 0 1
  • 使用爬蟲的時(shí)候記得進(jìn)入虛擬環(huán)境 查看已有的虛擬環(huán)境 workon 創(chuàng)建虛擬環(huán)境(創(chuàng)建完之后自動(dòng)進(jìn)入運(yùn)行虛擬環(huán)境) ...
    浮_屠閱讀 426評(píng)論 0 0