JavaScript 中 閉包 的詳解

閉包是什么

在 JavaScript 中园爷,閉包是一個(gè)讓人很難弄懂的概念按声。ECMAScript 中給閉包的定義是:閉包算凿,指的是詞法表示包括不被計(jì)算的變量的函數(shù),也就是說(shuō),函數(shù)可以使用函數(shù)之外定義的變量滚粟。

是不是看完這個(gè)定義感覺(jué)更加懵逼了?別急梯轻,我們來(lái)分析一下划鸽。

  • 閉包是一個(gè)函數(shù)
  • 閉包可以使用在它外面定義的變量
  • 閉包存在定義該變量的作用域中

好像有點(diǎn)清晰了,但是使用在它外面定義的變量是什么意思弦赖,我們先來(lái)看看變量作用域项栏。

變量作用域

變量可分為全局變量和局部變量。全局變量的作用域就是全局性的蹬竖,在 js 的任何地方都可以使用全局變量沼沈。在函數(shù)中使用 var 關(guān)鍵字聲明變量流酬,這時(shí)的變量即是局部變量,它的作用域只在聲明該變量的函數(shù)內(nèi)列另,在函數(shù)外面是訪問(wèn)不到該變量的芽腾。

var func = function(){
    var a = 'linxin';
    console.log(a);         // linxin
}
func();
console.log(a);             // Uncaught ReferenceError: a is not defined

作用域相對(duì)比較簡(jiǎn)單,我們不多講页衙,來(lái)看看跟閉包關(guān)系比較大的變量生存周期摊滔。

變量生存周期

全局變量,生命周期是永久的店乐。局部變量艰躺,當(dāng)定義該變量的函數(shù)調(diào)用結(jié)束時(shí),該變量就會(huì)被垃圾回收機(jī)制回收而銷毀眨八。再次調(diào)用該函數(shù)時(shí)又會(huì)重新定義了一個(gè)新變量腺兴。

var func = function(){
    var a = 'linxin';
    console.log(a);
}
func();

a 為局部變量,在 func 調(diào)用完之后踪古,a 就會(huì)被銷毀了含长。

var func = function(){
    var a = 'linxin';
    var func1 = function(){
        a += ' a';
        console.log(a);
    }
    return func1;
}
var func2 = func();
func2();                    // linxin a
func2();                    // linxin a a
func2();                    // linxin a a a

可以看出,在第一次調(diào)用完 func2 之后伏穆,func 中的變量 a 變成 'linxin a'拘泞,而沒(méi)有被銷毀。因?yàn)榇藭r(shí) func1 形成了一個(gè)閉包枕扫,導(dǎo)致了 a 的生命周期延續(xù)了陪腌。

這下子閉包就比較明朗了。

  • 閉包是一個(gè)函數(shù)烟瞧,比如上面的 func1 函數(shù)
  • 閉包使用其他函數(shù)定義的變量诗鸭,使其不被銷毀。比如上面 func1 調(diào)用了變量 a
  • 閉包存在定義該變量的作用域中参滴,變量 a 存在 func 的作用域中强岸,那么 func1 也必然存在這個(gè)作用域中。

現(xiàn)在可以說(shuō)砾赔,滿足這三個(gè)條件的就是閉包了蝌箍。

下面我們通過(guò)一個(gè)簡(jiǎn)單而又經(jīng)典的例子來(lái)進(jìn)一步熟悉閉包。

for (var i = 0; i < 4; i++) {
    setTimeout(function () {
        console.log(i)
    }, 0)
}

我們可能會(huì)簡(jiǎn)單的以為控制臺(tái)會(huì)打印出 0 1 2 3暴心,可事實(shí)卻打印出了 4 4 4 4妓盲,這又是為什么呢?我們發(fā)現(xiàn)专普,setTimeout 函數(shù)時(shí)異步的悯衬,等到函數(shù)執(zhí)行時(shí),for循環(huán)已經(jīng)結(jié)束了檀夹,此時(shí)的 i 的值為 4筋粗,所以 function() { console.log(i) } 去找變量 i策橘,只能拿到 4。

我們想起上一個(gè)例子中亏狰,閉包使 a 變量的值被保存起來(lái)了役纹,那么這里我們也可以用閉包把 0 1 2 3 保存起來(lái)偶摔。

for (var i = 0; i < 4; i++) {
    (function (i) {
        setTimeout(function () {
            console.log(i)
        }, 0)
    })(i)
}

當(dāng) i=0 時(shí)暇唾,把 0 作為參數(shù)傳進(jìn)匿名函數(shù)中,此時(shí) function(i){} 此匿名函數(shù)中的 i 的值為 0辰斋,等到 setTimeout 執(zhí)行時(shí)順著外層去找 i策州,這時(shí)就能拿到 0。如此循環(huán)宫仗,就能拿到想要的 0 1 2 3够挂。

內(nèi)存管理

在閉包中調(diào)用局部變量,會(huì)導(dǎo)致這個(gè)局部變量無(wú)法及時(shí)被銷毀藕夫,相當(dāng)于全局變量一樣會(huì)一直占用著內(nèi)存孽糖。如果需要回收這些變量占用的內(nèi)存,可以手動(dòng)將變量設(shè)置為null毅贮。

然而在使用閉包的過(guò)程中办悟,比較容易形成 JavaScript 對(duì)象和 DOM 對(duì)象的循環(huán)引用,就有可能造成內(nèi)存泄露滩褥。這是因?yàn)闉g覽器的垃圾回收機(jī)制中病蛉,如果兩個(gè)對(duì)象之間形成了循環(huán)引用,那么它們都無(wú)法被回收瑰煎。

function func() {
    var test = document.getElementById('test');
    test.onclick = function () {
        console.log('hello world');
    }
}

在上面例子中铺然,func 函數(shù)中用匿名函數(shù)創(chuàng)建了一個(gè)閉包。變量 test 是 JavaScript 對(duì)象酒甸,引用了 id 為 test 的 DOM 對(duì)象魄健,DOM 對(duì)象的 onclick 屬性又引用了閉包,而閉包又可以調(diào)用 test 插勤,因而形成了循環(huán)引用沽瘦,導(dǎo)致兩個(gè)對(duì)象都無(wú)法被回收。要解決這個(gè)問(wèn)題饮六,只需要把循環(huán)引用中的變量設(shè)為 null 即可其垄。

function func() {
    var test = document.getElementById('test');
    test.onclick = function () {
        console.log('hello world');
    }
    test = null;
}

如果在 func 函數(shù)中使用匿名函數(shù)創(chuàng)建閉包,而是通過(guò)引用一個(gè)外部函數(shù)卤橄,也不會(huì)出現(xiàn)循環(huán)引用的問(wèn)題绿满。

function func() {
    var test = document.getElementById('test');
    test.onclick = funcTest;
}
function funcTest(){
    console.log('hello world');
}

更多文章:lin-xin/blog

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市窟扑,隨后出現(xiàn)的幾起案子喇颁,更是在濱河造成了極大的恐慌漏健,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件橘霎,死亡現(xiàn)場(chǎng)離奇詭異蔫浆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)姐叁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門瓦盛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人外潜,你說(shuō)我怎么就攤上這事原环。” “怎么了处窥?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵嘱吗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我滔驾,道長(zhǎng)谒麦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任哆致,我火速辦了婚禮绕德,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沽瞭。我一直安慰自己迁匠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布驹溃。 她就那樣靜靜地躺著城丧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪豌鹤。 梳的紋絲不亂的頭發(fā)上亡哄,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音布疙,去河邊找鬼蚊惯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛灵临,可吹牛的內(nèi)容都是我干的截型。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼儒溉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宦焦!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤波闹,失蹤者是張志新(化名)和其女友劉穎酝豪,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體精堕,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡孵淘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了歹篓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘫证。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖滋捶,靈堂內(nèi)的尸體忽然破棺而出痛悯,到底是詐尸還是另有隱情,我是刑警寧澤重窟,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站惧财,受9級(jí)特大地震影響巡扇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜垮衷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一厅翔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧搀突,春花似錦刀闷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至徐许,卻和暖如春施蜜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背雌隅。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工翻默, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人恰起。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓修械,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親检盼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肯污,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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

  • 閉包(closure)是Javascript語(yǔ)言的一個(gè)難點(diǎn),也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)仇箱。 一县恕、變量...
    zouCode閱讀 1,275評(píng)論 0 13
  • 閉包(closure)是Javascript語(yǔ)言的一個(gè)難點(diǎn),也是它的特色剂桥,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)忠烛。 一、變量...
    zock閱讀 1,075評(píng)論 2 6
  • 作用域和閉包是 JavaScript 最重要的概念之一权逗,想要進(jìn)一步學(xué)習(xí) JavaScript美尸,就必須理解 Java...
    劼哥stone閱讀 1,181評(píng)論 1 13
  • 前言 對(duì)于js中的閉包,無(wú)論是老司機(jī)還是小白,我想,見(jiàn)得不能再多了,然而有時(shí)三言兩語(yǔ)卻很難說(shuō)得明白,反正在我初學(xué)時(shí)...
    itclanCoder閱讀 4,196評(píng)論 1 11
  • 偶然遇到簡(jiǎn)書,很像幾年前百思不得其解的創(chuàng)業(yè)念頭斟薇,從了他了师坎。
    一俗閱讀 153評(píng)論 0 0