js閉包淺析

什么是閉包

「函數(shù)」和「函數(shù)內(nèi)部能訪問(wèn)到的變量」(也叫環(huán)境)的總和盏缤,就是一個(gè)閉包修肠。

有些人說(shuō)閉包就是函數(shù)套函數(shù)古程,然后 return 一個(gè)函數(shù)。就像這樣:

function foo(){
  var local = 1;
  function bar(){
    local ++;
    return local;
  }
  return bar;
}

var func = foo()
console.log(func());

這里面確實(shí)有閉包屯伞,local 變量和 bar 函數(shù)就組成了一個(gè)閉包(Closure)

為什么要函數(shù)套函數(shù)呢腿箩?

是因?yàn)樾枰植孔兞浚圆虐?local 放在一個(gè)函數(shù)里劣摇,如果不把 local 放在一個(gè)函數(shù)里珠移,local 就是一個(gè)全局變量了,達(dá)不到使用閉包的目的——隱藏變量末融。

有些人看到「閉包」這個(gè)名字钧惧,就一定覺(jué)得要用什么包起來(lái)才行。其實(shí)這是翻譯問(wèn)題勾习,閉包的原文是 Closure浓瞪,跟「包」沒(méi)有任何關(guān)系。

所以函數(shù)套函數(shù)只是為了造出一個(gè)局部變量巧婶,跟閉包無(wú)關(guān)乾颁。

為什么要 return bar 呢?

因?yàn)槿绻?return艺栈,你就無(wú)法使用這個(gè)閉包英岭。把 return bar 改成 window.bar = bar 也是一樣的,只要讓外面可以訪問(wèn)到這個(gè) bar 函數(shù)就行了湿右。

所以 return bar 只是為了 bar 能被使用巴席,也跟閉包無(wú)關(guān)。

閉包會(huì)造成內(nèi)存泄露诅需?

錯(cuò)漾唉。

內(nèi)存泄露是指你用不到(訪問(wèn)不到)的變量荧库,依然占居著內(nèi)存空間,不能被再次利用起來(lái)赵刑。

閉包里面的變量明明就是我們需要的變量(lives)分衫,憑什么說(shuō)是內(nèi)存泄露?

這個(gè)謠言是如何來(lái)的般此?

因?yàn)?IE蚪战。IE 有 bug,IE 在我們使用完閉包之后铐懊,依然回收不了閉包里面引用的變量邀桑。

這是 IE 的問(wèn)題,不是閉包的問(wèn)題

現(xiàn)在我們來(lái)看一個(gè)關(guān)于閉包的題:

檢測(cè)一下我們對(duì)閉包的掌握的情況科乎。

function fun(n,o) {

  console.log(o)
  
  return {
  
    fun:function(m){
    
      return fun(m,n);
      
    }
    
  };
  
}

var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);//undefined,?,?,?

var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?

var c = fun(0).fun(1);  c.fun(2);  c.fun(3);//undefined,?,?,?

//問(wèn):三行a,b,c的輸出分別是什么壁畸?

//a: undefined,0,0,0
//b: undefined,0,1,2
//c: undefined,0,1,1

js函數(shù)的分類

具名函數(shù)(命名函數(shù))和匿名函數(shù)。

創(chuàng)建函數(shù)的幾種方式

1.聲明函數(shù)

functio fun(){ }

2.創(chuàng)建匿名函數(shù)表達(dá)式

var fun = function(){   }

//實(shí)際執(zhí)行順序?yàn)?// var fun;
// function fun(){  }

3.創(chuàng)建具名函數(shù)表達(dá)式

var fun = function funName(){   }

4.function構(gòu)造函數(shù)

可以給 Function 構(gòu)造函數(shù)傳一個(gè)函數(shù)字符串茅茂,返回包含這個(gè)字符串命令的函數(shù)捏萍,此種方法創(chuàng)建的是匿名函數(shù)。

5.自執(zhí)行函數(shù)

(function(){alert(1)})();
(function fn1(){alert(1);})();

分析上述三個(gè)函數(shù)的關(guān)系

function fun(n,o) { //標(biāo)準(zhǔn)具名函數(shù)空闲,返回的的是一個(gè)對(duì)象字面量表達(dá)式
  console.log(o)
  return {
    fun:function(m){ //匿名函數(shù)表達(dá)式
      //...
    }
  };
}

函數(shù)作用域鏈的問(wèn)題

1.對(duì)象內(nèi)部的函數(shù)表達(dá)式

var obj = {
    fn:function(){
        console.log(fn);
    }
}

obj.fn() //error(fn is not defined)

2.非對(duì)象內(nèi)部函數(shù)表達(dá)式

var fn = function(){
    console.log(fn)
}
fn() //function (){console.log(fn);};

結(jié)論是:使用var或是非對(duì)象內(nèi)部的函數(shù)表達(dá)式內(nèi)令杈,可以訪問(wèn)到存放當(dāng)前函數(shù)的變量;在對(duì)象內(nèi)部的不能訪問(wèn)到碴倾。

原因也非常簡(jiǎn)單逗噩,因?yàn)楹瘮?shù)作用域鏈的問(wèn)題,采用var的是在外部創(chuàng)建了一個(gè)fn變量跌榔,函數(shù)內(nèi)部當(dāng)然可以在內(nèi)部尋找不到fn后向上冊(cè)作用域查找fn异雁,而在創(chuàng)建對(duì)象內(nèi)部時(shí),因?yàn)闆](méi)有在函數(shù)作用域內(nèi)創(chuàng)建fn矫户,所以無(wú)法訪問(wèn)片迅。

所以綜上所述,可以得知皆辽,最內(nèi)層的return出去的fun函數(shù)不是第二層fun函數(shù)柑蛇,是最外層的fun函數(shù)。

到底在調(diào)用那個(gè)函數(shù)

function fun(n,o) {
  console.log(o)
  return {
    fun:function(m){
      return fun(m,n);
    }
  };
}
var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1);  c.fun(2);  c.fun(3);//undefined,?,?,?
//問(wèn):三行a,b,c的輸出分別是什么驱闷?

第一行:

var a = fun(0);  a.fun(1);  a.fun(2);   a.fun(3)

可以得知耻台,第一個(gè)fun(0)是在調(diào)用第一層fun函數(shù)。第二個(gè)fun(1)是在調(diào)用前一個(gè)fun的返回值的fun函數(shù)空另,所以:

第后面幾個(gè)fun(1),fun(2),fun(3),函數(shù)都是在調(diào)用第二層fun函數(shù)盆耽。

遂:

在第一次調(diào)用fun(0)時(shí),o為undefined;

第二次調(diào)用fun(1)時(shí)m為1摄杂,此時(shí)fun閉包了外層函數(shù)的n坝咐,也就是第一次調(diào)用的n=0,即m=1析恢,n=0墨坚,并在內(nèi)部調(diào)用第一層fun函數(shù)fun(1,0);所以o為0;

第三次調(diào)用fun(2)時(shí)m為2映挂,但依然是調(diào)用a.fun泽篮,所以還是閉包了第一次調(diào)用時(shí)的n,所以內(nèi)部調(diào)用第一層的fun(2,0);所以o為0

第四次同理柑船;

即:最終答案為undefined,0,0,0

第二行

var b = fun(0).fun(1).fun(2).fun(3)

先從fun(0)開(kāi)始看帽撑,肯定是調(diào)用的第一層fun函數(shù);而他的返回值是一個(gè)對(duì)象鞍时,所以第二個(gè)fun(1)調(diào)用的是第二層fun函數(shù)亏拉,后面幾個(gè)也是調(diào)用的第二層fun函數(shù)。

遂:

在第一次調(diào)用第一層fun(0)時(shí),o為undefined;

第二次調(diào)用 .fun(1)時(shí)m為1忿墅,此時(shí)fun閉包了外層函數(shù)的n陡鹃,也就是第一次調(diào)用的n=0,即m=1吮旅,n=0溪烤,并在內(nèi)部調(diào)用第一層fun函數(shù)fun(1,0);所以o為0;

第三次調(diào)用 .fun(2)時(shí)m為2庇勃,此時(shí)當(dāng)前的fun函數(shù)不是第一次執(zhí)行的返回對(duì)象檬嘀,而是第二次執(zhí)行的返回對(duì)象。而在第二次執(zhí)行第一層fun函數(shù)時(shí)時(shí)(1,0)所以n=1,o=0,返回時(shí)閉包了第二次的n责嚷,遂在第三次調(diào)用第三層fun函數(shù)時(shí)m=2,n=1鸳兽,即調(diào)用第一層fun函數(shù)fun(2,1),所以o為1罕拂;

第四次調(diào)用 .fun(3)時(shí)m為3揍异,閉包了第三次調(diào)用的n,同理爆班,最終調(diào)用第一層fun函數(shù)為fun(3,2)衷掷;所以o為2;

即最終答案:undefined,0,1,2

第三行

var c = fun(0).fun(1);  c.fun(2);  c.fun(3)

根據(jù)前面兩個(gè)例子柿菩,可以得知:

fun(0)為執(zhí)行第一層fun函數(shù)戚嗅,.fun(1)執(zhí)行的是fun(0)返回的第二層fun函數(shù),這里語(yǔ)句結(jié)束,遂c存放的是fun(1)的返回值懦胞,而不是fun(0)的返回值替久,所以c中閉包的也是fun(1)第二次執(zhí)行的n的值。c.fun(2)執(zhí)行的是fun(1)返回的第二層fun函數(shù)躏尉,c.fun(3)執(zhí)行的也是fun(1)返回的第二層fun函數(shù)侣肄。

遂:

在第一次調(diào)用第一層fun(0)時(shí),o為undefined醇份;

第二次調(diào)用 .fun(1)時(shí)m為1稼锅,此時(shí)fun閉包了外層函數(shù)的n,也就是第一次調(diào)用的n=0僚纷,即m=1矩距,n=0,并在內(nèi)部調(diào)用第一層fun函數(shù)fun(1,0);所以o為0怖竭;

第三次調(diào)用 .fun(2)時(shí)m為2锥债,此時(shí)fun閉包的是第二次調(diào)用的n=1,即m=2痊臭,n=1哮肚,并在內(nèi)部調(diào)用第一層fun函數(shù)fun(2,1);所以o為1;

第四次.fun(3)時(shí)同理广匙,但依然是調(diào)用的第二次的返回值允趟,遂最終調(diào)用第一層fun函數(shù)fun(3,1),所以o還為1

即最終答案:undefined,0,1,1

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鸦致,一起剝皮案震驚了整個(gè)濱河市潮剪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌分唾,老刑警劉巖抗碰,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異绽乔,居然都是意外死亡弧蝇,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)折砸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)看疗,“玉大人,你說(shuō)我怎么就攤上這事鞍爱【榫酰” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵睹逃,是天一觀的道長(zhǎng)盗扇。 經(jīng)常有香客問(wèn)我祷肯,道長(zhǎng),這世上最難降的妖魔是什么疗隶? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任佑笋,我火速辦了婚禮,結(jié)果婚禮上斑鼻,老公的妹妹穿的比我還像新娘蒋纬。我一直安慰自己,他們只是感情好坚弱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布蜀备。 她就那樣靜靜地躺著,像睡著了一般荒叶。 火紅的嫁衣襯著肌膚如雪碾阁。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天些楣,我揣著相機(jī)與錄音脂凶,去河邊找鬼。 笑死愁茁,一個(gè)胖子當(dāng)著我的面吹牛蚕钦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鹅很,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嘶居,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了道宅?” 一聲冷哼從身側(cè)響起食听,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤胸蛛,失蹤者是張志新(化名)和其女友劉穎污茵,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體葬项,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泞当,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了民珍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片襟士。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嚷量,靈堂內(nèi)的尸體忽然破棺而出陋桂,到底是詐尸還是另有隱情,我是刑警寧澤蝶溶,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布嗜历,位于F島的核電站宣渗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏梨州。R本人自食惡果不足惜痕囱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望暴匠。 院中可真熱鬧鞍恢,春花似錦、人聲如沸每窖。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)窒典。三九已至旭寿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間崇败,已是汗流浹背盅称。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留后室,地道東北人缩膝。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像岸霹,于是被迫代替她去往敵國(guó)和親疾层。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

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