前端學(xué)習(xí)筆記之閉包——看了一張圖終于明白啥是閉包了

閉包

閉包定義:指擁有多個(gè)變量和綁定了這些變量的環(huán)境的表達(dá)式(通常是一個(gè)函數(shù)),因而這些變量也是該表達(dá)式的一部分矾削。
函數(shù)內(nèi)部可以直接讀取全局變量侦厚。
函數(shù)內(nèi)部變量無(wú)法在函數(shù)外部訪問(wèn)健蕊。
函數(shù)內(nèi)部聲明要用var或者let聲明,不然會(huì)變成全局變量
鏈?zhǔn)阶饔糜颍鹤訉?duì)象會(huì)一級(jí)級(jí)向上尋找父對(duì)象的變量,父對(duì)象的變量子對(duì)象都是可見(jiàn)的缰猴,反之則不行吗购。
在一個(gè)閉包環(huán)境內(nèi)修改變量值,不會(huì)影響另一個(gè)閉包中的變量冰垄。
普通的函數(shù)內(nèi)嵌蹬癌,內(nèi)部函數(shù)是先執(zhí)行;而閉包則是:先把內(nèi)部函數(shù)賦給外部函數(shù)虹茶,然后在執(zhí)行逝薪。

下面這段代碼就是一根典型的閉包

function f1(){
    var a = 10;
    function f2(){
        alert(a);
    }
    f2();   //①
}
f1();       //10  ②

f1f1()的區(qū)別不加括號(hào)是代碼,加()是執(zhí)行這段代碼蝴罪,加return是返回一個(gè)值董济,可以把返回的值賦值給變量,不加return默認(rèn)返回undefined要门;

所以處有三種寫法:

第一種:處寫f2();虏肾,處調(diào)用需要這樣寫f1();。具體執(zhí)行過(guò)程:f1體內(nèi)調(diào)用f2函數(shù)欢搜,并執(zhí)行封豪。

第二種:處寫return f2();處調(diào)用需這樣寫f1();炒瘟。具體執(zhí)行過(guò)程:同上吹埠;區(qū)別是多了個(gè)return,因?yàn)楝F(xiàn)在f2函數(shù)中沒(méi)有返回值疮装,所以f1在調(diào)用f2只是執(zhí)行一下alert(a)缘琅,f1的返回值是undefined

第三種:處寫return f2;廓推,處調(diào)用需這樣寫f1()();胯杭。這里返回的是f2函數(shù)的代碼,所以在調(diào)用f1時(shí)要加上2個(gè)括號(hào)受啥,第一個(gè)括號(hào)是執(zhí)行f1函數(shù)做个,第2個(gè)括號(hào)是執(zhí)行f2函數(shù)鸽心,如果處省略return會(huì)報(bào)錯(cuò)。

return和函數(shù)調(diào)用時(shí)是否加括號(hào)的意思都明白居暖,但是把它倆結(jié)合起來(lái)顽频,就搞不清了。

正好今天學(xué)閉包時(shí)碰上了太闺,順便就把它搞清楚了糯景。

到底什么是閉包

對(duì)于新人(當(dāng)然了是說(shuō)我了),看很多閉包的定義省骂,代碼蟀淮,還是不知啥是閉包,云里霧里的钞澳,這里感謝方方老師的文章JS 中的閉包是什么怠惶?,看完后轧粟,雖然還是說(shuō)不出啥是閉包策治,但現(xiàn)在已經(jīng)知道啥是閉包了,果然用圖說(shuō)話最牛逼兰吟。(圖在文章中通惫,我就不放出來(lái)了)

閉包的應(yīng)用

MDN 上這個(gè)例子也寫的很好

image

調(diào)用Counter.value()時(shí),返回的是Counter內(nèi)部的變量privateCounter混蔼;
increment內(nèi)部沒(méi)有返回值履腋,這個(gè)方法只是執(zhí)行了privateCounter + 1操作,沒(méi)有返回值惭嚣;
同理decrement是將privateCounter - 1府树,也沒(méi)有返回值;
所以執(zhí)行Counter.increment料按,會(huì)返回undefined奄侠,但是接著操作Counter.value()時(shí)就可以得到1,因?yàn)閳?zhí)行上一步Counter.increment時(shí)privateCounter+1了载矿。

今天在下面三段代碼上花費(fèi)了大量的時(shí)間垄潮,一直似懂非懂,心里不踏實(shí)闷盔。

代碼一:

var name = 'window';
var obj = {
    name: 'object',
    getName: function() {
        return this.name;
    }
};
obj.getName(); //object
(obj.getName = obj.getName)(); //window 非嚴(yán)格模式下

代碼二:

var name = 'window';
var obj = {
    name: 'object',
    getName: function() {
        return function(){
            return this.name;    
        }
    }
};
obj.getName()(); //window

代碼三:

var name = 'window';
  var obj = {
    name : 'object',
    getName : function(){
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
obj.getName()();    //object

今天在看阮一峰的博客學(xué)習(xí)Javascript閉包(Closure)弯洗,對(duì)代碼二、代碼三部分很是不解逢勾,看到一網(wǎng)友搬出犀牛書(還沒(méi)看過(guò)牡整,我買了紅寶石書才看了一點(diǎn)點(diǎn))里的話,實(shí)在不解什么是作為函數(shù)調(diào)用溺拱,什么是作為方法調(diào)用逃贝;

《Javascript權(quán)威指南》上說(shuō):如果嵌套函數(shù)作為函數(shù)調(diào)用谣辞,其this值不是全局對(duì)象(非嚴(yán)格模式下)就是undefined(嚴(yán)格模式下); 如果嵌套函數(shù)作為方法調(diào)用,其this值指向調(diào)用它的對(duì)象沐扳。

又有一位網(wǎng)友說(shuō)

每個(gè)函數(shù)在被調(diào)用時(shí)泥从,其活動(dòng)對(duì)象都會(huì)自動(dòng)取得兩個(gè)特殊變量:this和arguments。內(nèi)部函數(shù)在搜索這個(gè)變量時(shí)沪摄,只會(huì)搜索到其活動(dòng)對(duì)象為止躯嫉,因此永遠(yuǎn)不可能直接訪問(wèn)外部函數(shù)中的這兩個(gè)變量(這一點(diǎn)通過(guò)前面的圖可以看得更清楚)。意思就是說(shuō)找到匿名函數(shù)中的this和arguments就不會(huì)再往下找了(這里的往下指的是外層的包含函數(shù)杨拐,和最外層的window全局環(huán)境)祈餐,而匿名函數(shù)的this對(duì)象通常指向window,所以輸出的是全局的那個(gè)字符串哄陶。不過(guò)帆阳,把外部作用域中的this對(duì)象保存在一個(gè)閉包能夠訪問(wèn)到的變量里,就可以讓閉包訪問(wèn)該對(duì)象了奕筐。

看到這里大概明白匿名函數(shù)的作用域是全局,繼續(xù)翻看下面評(píng)論变骡,大概意思是說(shuō)“把this保存在obj作用域下的一個(gè)變量中离赫,this就在當(dāng)前函數(shù)的作用域下了”。直到看完也是塌碌,似懂非懂渊胸,反正就是感覺(jué)哪里不對(duì)勁,但也說(shuō)不上了台妆。

直到看到【JavaScript】【函數(shù)】閉包閉包翎猛!這篇文章的代碼一部分,終于明白其中的邏輯了接剩。

下面就來(lái)分析其中的邏輯切厘,我分析的方法就是把不懂的地方一個(gè)個(gè)用console打印出來(lái)

代碼二和代碼一的區(qū)別是多了一層嵌套函數(shù),this值就不一樣了懊缺。
ps:我一開(kāi)始以為在代碼二中再嵌套一層函數(shù)疫稿,就會(huì)打印出object =_=|||

先來(lái)看代碼一,明白之后鹃两,另外兩段代碼自然就懂了遗座。

為什么obj.getName()打印出來(lái)的是object,因?yàn)檫@時(shí)getName方法是在obj的作用域下俊扳,所以this指向obj途蒋,返回值當(dāng)然就是object了。

接著看(obj.getName = obj.getName)()刪掉右邊后馋记,打印出的結(jié)果變成了object号坡,這就納悶了懊烤。
ps:第一眼看上去,這啥玩意筋帖,把自己賦值給自己奸晴?這不是多此一舉,直接用不就行了日麸!

console.log打印出obj.getName后寄啼,終于撥云見(jiàn)天,obj.getName = obj.getName這句話的意思就是把getName函數(shù)賦值給自己代箭,這個(gè)時(shí)候就不是obj.getName墩划,而是getName匿名函數(shù)了,匿名函數(shù)通常用的方法是()()立即執(zhí)行嗡综,此時(shí)再看匿名函數(shù)已經(jīng)脫離obj了乙帮,當(dāng)然this也就指向了全局,打印出window极景。

再來(lái)看代碼二察净,用console打印出obj.getName()會(huì)發(fā)現(xiàn)是一個(gè)匿名函數(shù),而匿名函數(shù)的this通常會(huì)指向全局盼樟,所以也就不難理解了

理解上面兩段代碼氢卡,代碼三也就很好理解了。

閉包中引用循環(huán)變量

廖雪峰的閉包在文中就很形象的講解了函數(shù)中的引用會(huì)變化的變量會(huì)有什么后果晨缴,我節(jié)選了他的結(jié)論和代碼译秦。

返回閉包時(shí)牢記的一點(diǎn)就是:返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會(huì)發(fā)生變化的變量击碗。
如果一定要引用循環(huán)變量怎么辦筑悴?方法是再創(chuàng)建一個(gè)函數(shù),用該函數(shù)的參數(shù)綁定循環(huán)變量當(dāng)前的值稍途,無(wú)論該循環(huán)變量后續(xù)如何更改阁吝,已綁定到函數(shù)參數(shù)的值不變:

function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push((function (n) {
            return function () {
                return n * n;
            }
        })(i));
    }
    return arr;
}

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

f1(); // 1
f2(); // 4
f3(); // 9

這里的核心就是立即執(zhí)行,如果不是立即執(zhí)行的話械拍,變量i就是for循環(huán)結(jié)束后的值了求摇。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市殊者,隨后出現(xiàn)的幾起案子与境,更是在濱河造成了極大的恐慌,老刑警劉巖猖吴,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摔刁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡海蔽,警方通過(guò)查閱死者的電腦和手機(jī)共屈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門绑谣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人拗引,你說(shuō)我怎么就攤上這事借宵。” “怎么了矾削?”我有些...
    開(kāi)封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵壤玫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我哼凯,道長(zhǎng)欲间,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任断部,我火速辦了婚禮猎贴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蝴光。我一直安慰自己她渴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布蔑祟。 她就那樣靜靜地躺著趁耗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪做瞪。 梳的紋絲不亂的頭發(fā)上对粪,一...
    開(kāi)封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天右冻,我揣著相機(jī)與錄音装蓬,去河邊找鬼。 笑死纱扭,一個(gè)胖子當(dāng)著我的面吹牛牍帚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播乳蛾,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼暗赶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了肃叶?” 一聲冷哼從身側(cè)響起蹂随,我...
    開(kāi)封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎因惭,沒(méi)想到半個(gè)月后岳锁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蹦魔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年激率,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了咳燕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乒躺,死狀恐怖招盲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嘉冒,我是刑警寧澤曹货,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站健爬,受9級(jí)特大地震影響控乾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜娜遵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一蜕衡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧设拟,春花似錦慨仿、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至跑慕,卻和暖如春万皿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背核行。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工牢硅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芝雪。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓减余,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親惩系。 傳聞我的和親對(duì)象是個(gè)殘疾皇子位岔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

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

  • 很多年了,一直做著一個(gè)有關(guān)于你的夢(mèng)堡牡。 夢(mèng)里抒抬,時(shí)常是一個(gè)燈光閃耀的舞臺(tái),一段清淺的樂(lè)曲晤柄,一個(gè)翩翩而來(lái)的舞者擦剑,...
    五月慕晴閱讀 629評(píng)論 0 1
  • 姓名:黃淑宜 公司:珠海三環(huán)知識(shí)產(chǎn)權(quán) 2017年10月1日打卡 第292A期樂(lè)觀三組 日精進(jìn)打卡第28天 【知~學(xué)...
    淑宜閱讀 218評(píng)論 0 0
  • hello! everybody ! 在經(jīng)歷人人生中最重要的考試后,緊張的身心得以放松,所以…… 我與丹姐...
    傲嬌祺閱讀 212評(píng)論 0 0
  • 細(xì)雨織春與君別 聽(tīng)不得雨聲紛沓檐前落 云碎柔腸珠淚脫 忍不住窗幔微啟窺春色 乍現(xiàn)玉樹翩粉蝶 喜斯雨亦新 驚斯花亦奇...
    水璇閱讀 255評(píng)論 0 4