閉包

什么是閉包

  • 了解什么是閉包之前浮定,要先了解變量的作用域征讲,變量的作用域無非就是兩種:全局變量和局部變量。
    Javascript語言的特殊之處欺殿,就在于函數(shù)內(nèi)部可以直接讀取全局變量寄纵。
      var n=999;
  function f1(){
    alert(n);
  }
  f1(); // 999

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

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

這里有一個地方需要注意程拭,函數(shù)內(nèi)部聲明變量的時候,一定要使用var命令棍潘。如果不用的話恃鞋,你實際上聲明了一個全局變量!

  function f1(){
    n=999;
  }
  f1();
  alert(n); // 999
  • 如何從外部讀取局部變量
    出于種種原因亦歉,我們有時候需要得到函數(shù)內(nèi)的局部變量恤浪。但是,前面已經(jīng)說過了鳍徽,正常情況下资锰,這是辦不到的,只有通過變通方法才能實現(xiàn)阶祭。
    那就是在函數(shù)的內(nèi)部绷杜,再定義一個函數(shù)。
  function f1(){
    var n=999;
    function f2(){      alert(n); // 999    }
  }

在上面的代碼中濒募,函數(shù)f2就被包括在函數(shù)f1內(nèi)部鞭盟,這時f1內(nèi)部的所有局部變量,對f2都是可見的瑰剃。但是反過來就不行齿诉,f2內(nèi)部的局部變量,對f1就是不可見的。這就是Javascript語言特有的"鏈?zhǔn)阶饔糜?結(jié)構(gòu)(chain scope)粤剧,子對象會一級一級地向上尋找所有父對象的變量歇竟。所以,父對象的所有變量抵恋,對子對象都是可見的焕议,反之則不成立。
既然f2可以讀取f1中的局部變量弧关,那么只要把f2作為返回值盅安,我們不就可以在f1外部讀取它的內(nèi)部變量了嗎!

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

上一節(jié)代碼中的f2函數(shù)世囊,就是閉包别瞭。
各種專業(yè)文獻上的"閉包"(closure)定義非常抽象,很難看懂株憾。我的理解是蝙寨,閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)
由于在Javascript語言中号胚,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量籽慢,因此可以把閉包簡單理解成"定義在一個函數(shù)內(nèi)部的函數(shù)"。
所以猫胁,在本質(zhì)上箱亿,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁。

  • 我們來看這么一段代碼
function add(){
  var i = 0;
  return function(){
  alert(i++);
  }
}
var f = add();
f();
f();

首先我們從作用域的角度來描述它弃秆,我們先定義一個全局的作用域届惋,然后我們看add的詞法環(huán)境。


當(dāng)我們執(zhí)行add的時候菠赚,它實際上返回了一個函數(shù)脑豹,這個函數(shù)對象在JS里面怎么描述?
我們知道衡查,當(dāng)一個函數(shù)創(chuàng)建的時候瘩欺,在引擎內(nèi)部會創(chuàng)建一個函數(shù)對象,這個函數(shù)里面會包含形參拌牲,函數(shù)體的內(nèi)容以及會保存當(dāng)前的應(yīng)用環(huán)境俱饿。
也就是說,當(dāng)我們返回的這個函數(shù)塌忽,它會保存當(dāng)前add的作用域環(huán)境拍埠,即它的scope會指向add environment。
當(dāng)我們執(zhí)行f函數(shù)的時候土居,首先也會創(chuàng)建這個函數(shù)的詞法作用域枣购,我們暫且成為closure environment嬉探。同時,我們也會執(zhí)行i++的操作棉圈。此時我們需要尋找變量i涩堤,實際上就在它的外層(outer)的作用域里面,也就說分瘾,當(dāng)我們執(zhí)行i++的時候定躏,它順著作用域鏈找到了add environment里面的i,找到之后給它加了1芹敌。

同理,繼續(xù)執(zhí)行f后垮抗,i就變成2氏捞。
一般來說,函數(shù)執(zhí)行完以后冒版,內(nèi)部的局部變量會被釋放掉液茎,等同于不存在了,但是JS里面比較特殊辞嗡,它允許函數(shù)作為返回值捆等,此時函數(shù)有可能引用外部變量,這時它就保存了對外部詞法環(huán)境的引用续室,這就是閉包的特性栋烤。

  • 總結(jié)如下
    • 閉包由函數(shù)和其相關(guān)的引用環(huán)境的組合而成
    • 閉包允許函數(shù)訪問其引用環(huán)境中的變量(又稱為自由變量)
    • 廣義上來說,所有JS函數(shù)都可以稱為閉包挺狰,因為JS
    • 函數(shù)在創(chuàng)建時保存了當(dāng)前的詞法環(huán)境

閉包的應(yīng)用-保存變量現(xiàn)場

  • 我們首先來看這段代碼



    這段代碼是用來做事件注冊明郭,在addHandeler里面呢,它會為一組元素去注冊onclick事件丰泊,當(dāng)onclick觸發(fā)的時候薯定,它可以把元素索引打出來,即希望當(dāng)我點到第零個元素的時候瞳购,alert出來0话侄,第一個元素的時候,alert出來1学赛。
    我們還是從作用域開始年堆,我們看到onclick這里的i其實是addHandler里面的i,不管循環(huán)體執(zhí)行多少次罢屈,onclick產(chǎn)生的函數(shù)訪問的i都是最外層的i嘀韧,也就是說,當(dāng)一個函數(shù)改變里面i的值缠捌,i的值就會跟這邊锄贷,那么最后我們?nèi)ピL問i的時候译蒂,i的值就已經(jīng)變了,因為這個循環(huán)在不斷的對i++谊却,最后我們onclick的時候得到的i值柔昼,會變成node.length而不是01234。

  • 我們再來看這段代碼



    這里我們把node[i].onclick賦給了helper函數(shù)炎辨,helper返回了一個函數(shù)捕透,可以看出helper是閉包的作用,i是形參碴萧,可以被內(nèi)部函數(shù)訪問乙嘀,也就是可以被里面的函數(shù)所訪問,helper都會產(chǎn)生閉包保存當(dāng)前的值破喻,當(dāng)我們執(zhí)行函數(shù)的時候虎谢,就會彈出當(dāng)時傳進去的值,從而到達保存變量現(xiàn)場的目的

閉包的應(yīng)用-封裝

  • 做信息隱藏曹质,有些變量我們不能暴露給外部婴噩,或者說,我們通過自執(zhí)行函數(shù)+閉包方法減少一些不必要的全局變量羽德,但是同時又能夠使這些變量在各自的自執(zhí)行函數(shù)內(nèi)有效几莽。


  • 這里我們observer函數(shù)里面返回了一組屬性,這些屬性都是接口宅静,正常來說我們是通過外部是訪問不到observer里面的observerList這個變量的章蚣,然而我們可以通過這些接口來訪問observerList這個變量

使用閉包的注意點

  • 由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大坏为,所以不能濫用閉包究驴,否則會造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露匀伏。解決方法是洒忧,在退出函數(shù)之前,將不使用的局部變量全部刪除够颠。
  • 閉包會在父函數(shù)外部熙侍,改變父函數(shù)內(nèi)部變量的值。所以履磨,如果你把父函數(shù)當(dāng)作對象(object)使用蛉抓,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value)剃诅,這時一定要小心巷送,不要隨便改變父函數(shù)內(nèi)部變量的值。

思考題

如果你能理解下面兩段代碼的運行結(jié)果矛辕,應(yīng)該就算理解閉包的運行機制了笑跛。

  • 代碼片段一
  var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());
  • 代碼片段二
  var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
  alert(object.getNameFunc()());

內(nèi)容參考:學(xué)習(xí)Javascript閉包(Closure)付魔,從外部讀取局部變量

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市飞蹂,隨后出現(xiàn)的幾起案子几苍,更是在濱河造成了極大的恐慌,老刑警劉巖陈哑,帶你破解...
    沈念sama閱讀 211,423評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妻坝,死亡現(xiàn)場離奇詭異,居然都是意外死亡惊窖,警方通過查閱死者的電腦和手機刽宪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來界酒,“玉大人纠屋,你說我怎么就攤上這事《芗疲” “怎么了?”我有些...
    開封第一講書人閱讀 157,019評論 0 348
  • 文/不壞的土叔 我叫張陵赁遗,是天一觀的道長署辉。 經(jīng)常有香客問我,道長岩四,這世上最難降的妖魔是什么哭尝? 我笑而不...
    開封第一講書人閱讀 56,443評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮剖煌,結(jié)果婚禮上材鹦,老公的妹妹穿的比我還像新娘。我一直安慰自己耕姊,他們只是感情好桶唐,可當(dāng)我...
    茶點故事閱讀 65,535評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著茉兰,像睡著了一般尤泽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上规脸,一...
    開封第一講書人閱讀 49,798評論 1 290
  • 那天坯约,我揣著相機與錄音,去河邊找鬼莫鸭。 笑死闹丐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的被因。 我是一名探鬼主播卿拴,決...
    沈念sama閱讀 38,941評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼衫仑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了巍棱?” 一聲冷哼從身側(cè)響起惑畴,我...
    開封第一講書人閱讀 37,704評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎航徙,沒想到半個月后如贷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,152評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡到踏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,494評論 2 327
  • 正文 我和宋清朗相戀三年杠袱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窝稿。...
    茶點故事閱讀 38,629評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡楣富,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伴榔,到底是詐尸還是另有隱情纹蝴,我是刑警寧澤,帶...
    沈念sama閱讀 34,295評論 4 329
  • 正文 年R本政府宣布踪少,位于F島的核電站塘安,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏援奢。R本人自食惡果不足惜兼犯,卻給世界環(huán)境...
    茶點故事閱讀 39,901評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望集漾。 院中可真熱鬧切黔,春花似錦、人聲如沸具篇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驱显。三九已至险领,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間秒紧,已是汗流浹背绢陌。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留熔恢,地道東北人脐湾。 一個月前我還...
    沈念sama閱讀 46,333評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像叙淌,于是被迫代替她去往敵國和親秤掌。 傳聞我的和親對象是個殘疾皇子愁铺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,499評論 2 348

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

  • ● 閉包基礎(chǔ) ● 閉包作用 ● 閉包經(jīng)典例子 ● 閉包應(yīng)用 ● 閉包缺點 ● 參考資料 1、閉包基礎(chǔ) 作用域和作...
    lzyuan閱讀 929評論 0 0
  • 閉包(closure)是Javascript語言的一個難點闻鉴,也是它的特色茵乱,很多高級應(yīng)用都要依靠閉包實現(xiàn)。 一孟岛、變量...
    zock閱讀 1,075評論 2 6
  • 閉包(closure)是Javascript語言的一個難點瓶竭,也是它的特色,很多高級應(yīng)用都要依靠閉包實現(xiàn)渠羞。 一斤贰、變量...
    zouCode閱讀 1,271評論 0 13
  • Javascript閉包的定義非常晦澀——閉包次询,是指語法域位于某個特定的區(qū)域荧恍,具有持續(xù)參照(讀寫)位于該區(qū)域內(nèi)自身...
    LiLi原上草閱讀 419評論 2 3
  • Désolée de répondre si tard.
    白天不賣豆腐閱讀 214評論 0 0