js之閉包

大多數(shù)面試官都會(huì)問你有關(guān)閉包是什么的問題同衣,而大多數(shù)時(shí)候你的一個(gè)錯(cuò)誤答案的代價(jià)就是失去一份工作。就算你夠幸運(yùn)的拿到了這份工作的 offer辛孵,你也會(huì)在年薪上無形損失上萬(wàn)美元厨疙。因?yàn)槟銜?huì)以初級(jí)開發(fā)工程師的身份被招進(jìn)公司,你的工作經(jīng)驗(yàn)有多久人家是不會(huì)在乎的轩娶。(掘金文章里說的)
(本文純屬個(gè)人學(xué)習(xí)記錄)

一儿奶、變量的作用域

要理解閉包,首先必須理解Javascript特殊的變量作用域鳄抒。
變量的作用域無非就是兩種:全局變量和局部變量闯捎。
Javascript語(yǔ)言的特殊之處搅窿,就在于函數(shù)內(nèi)部可以直接讀取全局變量。

   var n=999;
  function f1(){
    alert(n);
  }
  f1(); // 999

另一方面隙券,在函數(shù)外部自然無法讀取函數(shù)內(nèi)的局部變量男应。

    function f1(){
    var n=999;
  }
  alert(n); // error

這里有一個(gè)地方需要注意,函數(shù)內(nèi)部聲明變量的時(shí)候娱仔,一定要使用var命令沐飘。如果不用的話,你實(shí)際上聲明了一個(gè)全局變量牲迫!

    function f1(){
    n=999;
  }

  f1();
  alert(n); // 999
二耐朴、如何從外部讀取局部變量?

出于種種原因盹憎,我們有時(shí)候需要得到函數(shù)內(nèi)的局部變量筛峭。但是,前面已經(jīng)說過了陪每,正常情況下影晓,這是辦不到的,只有通過變通方法才能實(shí)現(xiàn)檩禾。

那就是在函數(shù)的內(nèi)部挂签,再定義一個(gè)函數(shù)。

   function f1(){
    var n=999;
    function f2(){
      alert(n); // 999
    }
  }

在上面的代碼中盼产,函數(shù)f2就被包括在函數(shù)f1內(nèi)部饵婆,這時(shí)f1內(nèi)部的所有局部變量,對(duì)f2都是可見的戏售。但是反過來就不行侨核,f2內(nèi)部的局部變量,對(duì)f1就是不可見的灌灾。這就是Javascript語(yǔ)言特有的"鏈?zhǔn)阶饔糜?結(jié)構(gòu)(chain scope)搓译,子對(duì)象會(huì)一級(jí)一級(jí)地向上尋找所有父對(duì)象的變量。所以紧卒,父對(duì)象的所有變量侥衬,對(duì)子對(duì)象都是可見的,反之則不成立跑芳。

既然f2可以讀取f1中的局部變量轴总,那么只要把f2作為返回值,我們不就可以在f1外部讀取它的內(nèi)部變量了嗎博个!

  function f1(){
    var n=999;
    function f2(){
      alert(n); 
    }
    return f2;
  }
  var result=f1();
  result(); // 999
三怀樟、什么是閉包

閉包即函數(shù)與其引用的周邊狀態(tài)(詞法環(huán)境)綁定在一起形成的(封裝)組合。換句話說盆佣,閉包可以讓我們從函數(shù)內(nèi)部訪問其外部函數(shù)的作用域往堡。在 JavaScript 中械荷,每當(dāng)函數(shù)創(chuàng)建,閉包就被創(chuàng)建虑灰。
為了使用閉包吨瞎,我們可以簡(jiǎn)單的將一個(gè)函數(shù)定義在另一個(gè)函數(shù)的內(nèi)部,然后將其暴露給外部穆咐,返回這個(gè)函數(shù)或者是把它傳給另一個(gè)函數(shù)颤诀。
內(nèi)部函數(shù)會(huì)擁有訪問外部函數(shù)作用域中變量的能力,即使是外部函數(shù)已經(jīng)執(zhí)行完畢并銷毀对湃。
(在本質(zhì)上崖叫,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁)


簡(jiǎn)單最原始的閉包,以便讓你在大腦里產(chǎn)生閉包的畫面:

   function A() {
        function B() {
            console.log('Hello 閉包!');
        }
        return B;
    }
    var C = A();
    C();     // Hello 閉包!

這是最簡(jiǎn)單的閉包拍柒。

有了初步認(rèn)識(shí)后心傀,我們簡(jiǎn)單分析一下它和普通函數(shù)有什么不同,上面代碼翻譯成自然語(yǔ)言如下:

定義普通函數(shù) A
在 A 中定義普通函數(shù) B
在 A 中返回 B
執(zhí)行 A拆讯,并把 A 的返回結(jié)果賦值給變量 C
執(zhí)行 C
把這5步操作總結(jié)成一句話就是:

函數(shù)A的內(nèi)部函數(shù)B被函數(shù)A外的一個(gè)變量 c 引用脂男。
把這句話再加工一下就變成了閉包的定義:
當(dāng)一個(gè)內(nèi)部函數(shù)被其外部函數(shù)之外的變量引用時(shí),就形成了一個(gè)閉包往果。
因此疆液,當(dāng)你執(zhí)行上述5步操作時(shí),就已經(jīng)定義了一個(gè)閉包陕贮!
這就是閉包。

四潘飘、使用閉包

閉包可以用在許多地方肮之。它的最大用處有兩個(gè),一個(gè)是前面提到的可以讀取函數(shù)內(nèi)部的變量卜录,另一個(gè)就是讓這些變量的值始終保持在內(nèi)存中戈擒。

在了解閉包的作用之前,我們先了解一下 Javascript 中的 GC 機(jī)制:

在 Javascript 中艰毒,如果一個(gè)對(duì)象不再被引用筐高,那么這個(gè)對(duì)象就會(huì)被 GC 回收,否則這個(gè)對(duì)象一直會(huì)保存在內(nèi)存中丑瞧。

在上述例子中柑土,B 定義在 A 中,因此 B 依賴于 A ,而外部變量 C 又引用了 B , 所以A間接的被 C 引用绊汹。

也就是說稽屏,A 不會(huì)被 GC 回收,會(huì)一直保存在內(nèi)存中西乖。為了證明我們的推理狐榔,上面的例子稍作改進(jìn):

function A() {
    var count = 0;
    function B() {
       count ++;
       console.log(count);
    }
    return B;
}
var C = A();
C();// 1
C();// 2
C();// 3

count 是函數(shù)A 中的一個(gè)變量坛增,它的值在函數(shù)B 中被改變,函數(shù) B 每執(zhí)行一次薄腻,count 的值就在原來的基礎(chǔ)上累加 1 收捣。因此,函數(shù)A中的 count 變量會(huì)一直保存在內(nèi)存中庵楷。

當(dāng)我們需要在模塊中定義一些變量罢艾,并希望這些變量一直保存在內(nèi)存中但又不會(huì) “污染” 全局的變量時(shí),就可以用閉包來定義這個(gè)模塊嫁乘。

五昆婿、另外

var name = "The Window";
    var object = {
        name: "My Object",
        getNameFunc: function () {
            return function () {
                return this.name;
            };
        }
    }
    alert(object.getNameFunc()());    //The Window
    var name = "The Window";
    var object = {
        name: "My Object",
        getNameFunc: function () {
            var that = this;
            return function () {
                return that.name;
            };
        }
    };
    alert(object.getNameFunc()());    //My Object
 因?yàn)榈谝活}中g(shù)etNameFunc這個(gè)方法或者叫函數(shù)是屬于全局作用域的,所以里面返回的this始終都是指向window的蜓斧。
 而第二題中用that=this改變了當(dāng)前函數(shù)指向的作用域仓蛆,所以第二題中的this最終只想的是myobject。

六挎春、使用閉包的注意點(diǎn)

1)由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中看疙,內(nèi)存消耗很大,所以不能濫用閉包直奋,否則會(huì)造成網(wǎng)頁(yè)的性能問題能庆,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是脚线,在退出函數(shù)之前搁胆,將不使用的局部變量全部刪除。

2)閉包會(huì)在父函數(shù)外部邮绿,改變父函數(shù)內(nèi)部變量的值渠旁。所以,如果你把父函數(shù)當(dāng)作對(duì)象(object)使用船逮,把閉包當(dāng)作它的公用方法(Public Method)顾腊,把內(nèi)部變量當(dāng)作它的私有屬性(private value),這時(shí)一定要小心挖胃,不要隨便改變父函數(shù)內(nèi)部變量的值杂靶。

參考:
https://juejin.im/post/5cfd11fbe51d4555fd20a30d
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
https://www.cnblogs.com/onepixel/p/5062456.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市酱鸭,隨后出現(xiàn)的幾起案子吗垮,更是在濱河造成了極大的恐慌,老刑警劉巖凛辣,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抱既,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡扁誓,警方通過查閱死者的電腦和手機(jī)防泵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門蚀之,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人捷泞,你說我怎么就攤上這事足删。” “怎么了锁右?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵失受,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我咏瑟,道長(zhǎng)拂到,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任码泞,我火速辦了婚禮兄旬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘余寥。我一直安慰自己领铐,他們只是感情好蠢正,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布局服。 她就那樣靜靜地躺著,像睡著了一般暴氏。 火紅的嫁衣襯著肌膚如雪祝蝠。 梳的紋絲不亂的頭發(fā)上音诈,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音绎狭,去河邊找鬼改艇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛坟岔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播摔桦,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼社付,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了邻耕?” 一聲冷哼從身側(cè)響起鸥咖,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兄世,沒想到半個(gè)月后啼辣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡御滩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年鸥拧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了党远。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡富弦,死狀恐怖沟娱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腕柜,我是刑警寧澤济似,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站盏缤,受9級(jí)特大地震影響砰蠢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜唉铜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一台舱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧打毛,春花似錦柿赊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至熬甫,卻和暖如春胰挑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背椿肩。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工瞻颂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人郑象。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓贡这,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親厂榛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盖矫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355