Javascript基礎(chǔ)--閉包

寫在前面

閉包是JavaScript中一個(gè)重要的概念,本文使用3w(what拧略,why芦岂,how)原則總結(jié)一下閉包這個(gè)概念。

what垫蛆,什么是閉包禽最?

網(wǎng)上關(guān)于閉包的定義有很多腺怯,但大多過(guò)于繁雜。一種簡(jiǎn)潔的說(shuō)法:閉包是一個(gè)有狀態(tài)的函數(shù)(a stateful function)---- 首先川无,閉包是一個(gè)函數(shù)呛占,它一定存在于外層函數(shù)內(nèi);其次懦趋,閉包“記住了”其外層函數(shù)擁有的變量晾虑,它能夠訪問(wèn)外層函數(shù)的作用域仅叫。
下面的例子就形成了一個(gè)閉包:

function foo() {
    var data = "hello";
    function bar() {
        console.log(data);  // 打印 hello
    }
    return bar;
}
foo()();

函數(shù)bar就是一個(gè)閉包帜篇,它能夠訪問(wèn)外層函數(shù)foo的變量data。當(dāng)調(diào)用foo()()時(shí)诫咱,也就是調(diào)用了bar函數(shù)笙隙,將打印foo函數(shù)內(nèi)部變量data的值。

why坎缭,為什么要用閉包竟痰,作用是什么?

閉包多用來(lái)提供“模塊化”掏呼,起到“封裝”代碼的目的凯亮。
由于外部執(zhí)行環(huán)境無(wú)法訪問(wèn)函數(shù)內(nèi)部的變量,但可以通過(guò)將閉包暴露出來(lái)的方式哄尔,使外部獲取對(duì)函數(shù)內(nèi)部變量的訪問(wèn)權(quán)假消。
閉包的這個(gè)用法,類似于C++岭接、Java等語(yǔ)言的公有函數(shù)(public)富拗,提供外界訪問(wèn)類的私有成員變量(private)的能力。
下面是一個(gè)閉包提供的例子:

function Person() {
  var name = "Joe";
  return {
    getName: function() {
      return name;
    },
    setName: function(newName) {
      name = newName;
    }
  }
}

var p = Person();
console.log(p.name);  // 打印undefined
console.log(p.getName()); // 打印Joe
p.setName('Mike');
console.log(p.getName()); // 打印Mike

Person函數(shù)的內(nèi)部變量name只存在于Person的函數(shù)作用域內(nèi)鸣戴,無(wú)法被外界訪問(wèn)到啃沪,所以打印undefined。
通過(guò)閉包getName和setName窄锅,可以達(dá)到訪問(wèn)變量name的目的创千。

在es6中終于有了類的概念,上面的代碼可以用類的方式改寫:

class Person {
  constructor() {
    this.name = 'Joe';
  }
  getName() {
    return this.name;
  }
  setName(newName) {
    this.name = newName;
  }
}

var p = new Person();
console.log(p.name);  // 打印Joe
console.log(p.getName()); // 打印Joe
p.setName('Mike');
console.log(p.getName()); // 打印Mike

es6由于沒(méi)有private概念入偷,可以看到這里的name屬性其實(shí)是可以被外界直接調(diào)用到的追驴。

另外在回調(diào)函數(shù)(如callback,setTimeout)疏之、異步執(zhí)行的函數(shù)(如ajax請(qǐng)求)中殿雪,經(jīng)常看到閉包的身影锋爪。閉包一定程度上簡(jiǎn)化了代碼的寫法丙曙。

how爸业,閉包是如何形成的?

說(shuō)起閉包的底層機(jī)制亏镰,就首先要搞懂js語(yǔ)言中的執(zhí)行環(huán)境和作用域鏈的概念扯旷,這篇文章介紹了這些概念。
閉包的原理其實(shí)就是形成閉包的這個(gè)內(nèi)層函數(shù)索抓,在其自身的作用域鏈上钧忽,頭結(jié)點(diǎn)(活動(dòng)對(duì)象)是自身的變量對(duì)象,記錄了自身內(nèi)部的變量信息纸兔;而作用域鏈上的第二個(gè)節(jié)點(diǎn)是其外層函數(shù)的變量對(duì)象,記錄了外層函數(shù)的變量信息----很顯然否副,這個(gè)節(jié)點(diǎn)上保存的信息能夠被閉包函數(shù)訪問(wèn)的到汉矿,閉包定義所說(shuō)的“周圍環(huán)境”,其實(shí)就是這個(gè)數(shù)據(jù)备禀。
一般的洲拇,當(dāng)外層函數(shù)執(zhí)行完成后,js引擎會(huì)釋放掉它的執(zhí)行環(huán)境曲尸,作用域鏈等信息赋续。但如果其內(nèi)部含有閉包函數(shù),由于閉包函數(shù)的作用域鏈上仍然引用著外層函數(shù)的變量對(duì)象(第二個(gè)節(jié)點(diǎn))另患,垃圾回收器判定這塊內(nèi)存仍然有在被使用(被閉包引用)纽乱,就不會(huì)釋放掉這塊內(nèi)存,所以即使外層函數(shù)執(zhí)行完昆箕,我們依然能通過(guò)閉包函數(shù)鸦列,訪問(wèn)其外層函數(shù)的變量信息。經(jīng)常有說(shuō)閉包容易引起內(nèi)存泄漏鹏倘,也就是因?yàn)閮?nèi)存一直被閉包引用者薯嗤,無(wú)法被垃圾回收導(dǎo)致的結(jié)果。
閉包的這種底層實(shí)現(xiàn)纤泵,有時(shí)候會(huì)引起看上去有點(diǎn)預(yù)想不到的結(jié)果骆姐,比如在for循環(huán)中。思考下面的例子捏题,為什么打印出來(lái)的都是5呢玻褪?

function arrayFun() {
    var arr = [];
    for (var i = 0; i < 5; i++) {
        arr.push(function () {
            console.log(i);
        });
    }
    return arr;
}
var retArr = arrayFun();
for (var i = 0; i < 5; i++) {
    retArr[i]();   // 打印 5,5公荧,5归园,5,5
}

由于在es6之前稚矿,只有全局作用域和函數(shù)作用域庸诱,i變量在函數(shù)arrayFun中捻浦,只有一份副本。那么閉包(此處是個(gè)匿名函數(shù))在作用域鏈上引用的是同一個(gè)i值(i在執(zhí)行完for循環(huán)后值變?yōu)?)桥爽,當(dāng)調(diào)用閉包時(shí)朱灿,訪問(wèn)作用域鏈上的i值,就都是5了钠四。
在es6中引入了let和塊作用域盗扒,i的值在塊作用域中是不同的副本,所以就不會(huì)出現(xiàn)上面的情況了缀去。仍然是這篇文章有代碼實(shí)例侣灶,不再贅述。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末缕碎,一起剝皮案震驚了整個(gè)濱河市褥影,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌咏雌,老刑警劉巖凡怎,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異赊抖,居然都是意外死亡统倒,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門氛雪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)房匆,“玉大人,你說(shuō)我怎么就攤上這事报亩√陈疲” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵捆昏,是天一觀的道長(zhǎng)赚楚。 經(jīng)常有香客問(wèn)我,道長(zhǎng)骗卜,這世上最難降的妖魔是什么宠页? 我笑而不...
    開(kāi)封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮寇仓,結(jié)果婚禮上举户,老公的妹妹穿的比我還像新娘。我一直安慰自己遍烦,他們只是感情好俭嘁,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著服猪,像睡著了一般供填。 火紅的嫁衣襯著肌膚如雪拐云。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天近她,我揣著相機(jī)與錄音叉瘩,去河邊找鬼。 笑死粘捎,一個(gè)胖子當(dāng)著我的面吹牛薇缅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播攒磨,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼泳桦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了娩缰?” 一聲冷哼從身側(cè)響起灸撰,我...
    開(kāi)封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎漆羔,沒(méi)想到半個(gè)月后梧奢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狱掂,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡演痒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了趋惨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸟顺。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖器虾,靈堂內(nèi)的尸體忽然破棺而出讯嫂,到底是詐尸還是另有隱情,我是刑警寧澤兆沙,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布欧芽,位于F島的核電站,受9級(jí)特大地震影響葛圃,放射性物質(zhì)發(fā)生泄漏千扔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一库正、第九天 我趴在偏房一處隱蔽的房頂上張望曲楚。 院中可真熱鬧,春花似錦褥符、人聲如沸龙誊。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)趟大。三九已至鹤树,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間护昧,已是汗流浹背魂迄。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惋耙,地道東北人捣炬。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像绽榛,于是被迫代替她去往敵國(guó)和親湿酸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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