JS閉包的2W1H

閉包是JS中一個(gè)重要概念呐舔,很有用處币励,但不好理解。這里從what珊拼、why食呻、how三個(gè)方面來總結(jié)閉包知識(shí)。

1 WHAT (閉包是什么?)


閉包:函數(shù)(A)中的函數(shù)(B)仅胞,可以訪問外部函數(shù)(A)內(nèi)部的所有變量每辟。

2 WHY (為什么閉包能訪問函數(shù)變量?)


2.1 作用域鏈

要理解閉包原理干旧,需要先了解作用域鏈的細(xì)節(jié)渠欺。

function compare(value1,value2){
  if(value1<value2){
    return -1;
  }else if(value1>value2){
    return 1;
  }else{
    return 0;
  }
}
var result = compare(5,10);

上面代碼,創(chuàng)建compare()時(shí)椎眯,會(huì)創(chuàng)建包含全局變量對(duì)象(含this挠将、compare、result)的作用域鏈编整。

而第一次調(diào)用compare()時(shí)舔稀,會(huì)創(chuàng)建一個(gè)局部執(zhí)行環(huán)境。再創(chuàng)建compare()活動(dòng)對(duì)象(包含this掌测、arguments内贮、value1、value2)赏半,推入執(zhí)行環(huán)境作用域鏈前端贺归。故compare()活動(dòng)對(duì)象處于作用域鏈第一位(0),而全局變量對(duì)象處于作用域鏈的第二位(1)断箫。

通過作用域鏈拂酣,函數(shù)可以訪問局部變量和全局變量。函數(shù)執(zhí)行后仲义,局部活動(dòng)對(duì)象會(huì)被銷毀婶熬,內(nèi)存只保存全局變量對(duì)象。但閉包的情況又不一樣了埃撵。

2.2閉包的作用域鏈

function createComparisonFunction(propertyName){ 
    return function(object1, object2){    //閉包
       var value1 = object1[propertyName];//訪問外部函數(shù)的變量
       var value2 = object2[propertyName]; 
       if(value1 > value2){ 
         return 1;  
       }else if(value1 < value2){
         return -1; 
       }else{ 
         return 0; 
       }
   };
}
// 創(chuàng)建函數(shù)
var compareNames = createComparison("name");
// 調(diào)用函數(shù)
var result = compareNames({name:"Lillian"},{name:"Matthew"});

外部函數(shù)<code>createComparitionFunction()</code>的活動(dòng)對(duì)象會(huì)添加到閉包的作用域鏈中赵颅,這樣閉包就可以訪問函數(shù)中的變量。問題是暂刘,<code>createComparitionFunction()</code>執(zhí)行完畢后饺谬,閉包仍然引用著它的活動(dòng)對(duì)象,所以無法銷毀其活動(dòng)對(duì)象谣拣。

只有閉包銷毀募寨,才能銷毀函數(shù)的活動(dòng)對(duì)象,釋放內(nèi)存:

// 解除對(duì)函數(shù)(閉包)的引用森缠,釋放內(nèi)存
compareNames = null;

3 HOW (怎樣使用閉包拔鹰?)


閉包有很多用途,這里只列舉最常用的兩種贵涵。

3.1 實(shí)現(xiàn)私有作用域

JS沒有塊級(jí)作用域概念:

function outputNumbers(count){ 
   for(var i = 0; i< count; i++){
      alert(i); 
   }
 alert(i); // 不會(huì)報(bào)錯(cuò)
}

由于沒有塊級(jí)作用域列肢,for循環(huán)結(jié)束后恰画,<code>i</code>并不會(huì)被銷毀。所以<code>alert(i)</code>不會(huì)報(bào)錯(cuò)瓷马。

使用自調(diào)用函數(shù)可以模仿塊級(jí)作用域:

(function(){
    //這里是塊級(jí)作用域
})()

其實(shí)自調(diào)用函數(shù)實(shí)現(xiàn)私有作用域拴还,與閉包沒有必然聯(lián)系,只是自調(diào)用函數(shù)也可以用于函數(shù)內(nèi)部(作為閉包):

function outputNumbers(count){ 
   (function(){
      for(var i = 0; i< count; i++){
         alert(i); 
      }
   })()
 alert(i); // 報(bào)錯(cuò)欧聘,i沒有定義
}

For循環(huán)放在自調(diào)用函數(shù)(此處是閉包)中自沧,這樣變量<code>i</code>只能在循環(huán)中被訪問,在循環(huán)外部無法訪問树瞭。

3.2 訪問私有變量

在函數(shù)中定義的變量(參數(shù)、局部變量爱谁、內(nèi)部函數(shù))晒喷,都不能在外部訪問,所以是私有變量访敌。而閉包可以訪問函數(shù)中的變量凉敲,這就提供了訪問私有變量的共有方法(特權(quán)方法)。

對(duì)于對(duì)象來說寺旺,有下面幾種方式訪問私有變量:

(1) 構(gòu)造函數(shù)模式:

function MyObject() {
    //私有變量和私有函數(shù)
    var privateVariable = 10;
    function privateFunction(){
        return false;
    }
  //特權(quán)方法
    this.publicMethod = function() {
        privateVariable++;
        return privateFunction();
    };
}
var obj1 = new MyObject();
console.log(obj1.publicMethod());

創(chuàng)建<code>MyObject</code>的實(shí)例<code>obj1</code> 后爷抓,只能用<code>publicMethod() </code>訪問<code>privateVariable</code>和<code>privateFunction()</code>,沒有其他方法可以直接訪問私有變量和私有函數(shù)阻塑。

(2)原型模式:

(function(){
    //私有變量和私有函數(shù)
    var privateVariable = 10;
    function privateFunction() {
        return false;
    }
    //構(gòu)造函數(shù)
    MyObject = function(){
    };
     /*函數(shù)聲明只能創(chuàng)建局部函數(shù)蓝撇,所以這里使用了函數(shù)表達(dá)式。
    注意:變量MyObject沒有加var陈莽,所以是全局變量渤昌。在私有作用域外部也能訪問。*/

    //特權(quán)方法
    MyObject.prototype.publicMethod = function(){
        privateVariable++;
        return privateFunction();
    }
})();

原型模式與構(gòu)造函數(shù)模式最主要的區(qū)別就是私有變量和私有函數(shù)由實(shí)例共享的走搁。這樣独柑,變量就成了靜態(tài)的、由所有實(shí)例共享的屬性私植,即靜態(tài)私有變量忌栅。

(3)模塊模式:
模塊模式用于為單例創(chuàng)建私有變量和特權(quán)方法。(單例:只有一個(gè)實(shí)例的對(duì)象曲稼。)

var singleton = function(){
    //私有變量和私有函數(shù)
    var privateVariable = 10;
    function privateFunction(){
        return false;
    }
    //公有方法和屬性
    return {
        publicProperty: true,
        publicMethod: function(){
            privateVariable++;
            return privateFunction();
        }
    };
}();

該模式返回一個(gè)對(duì)象字面量索绪,包含公有屬性和方法。該對(duì)象是在匿名函數(shù)內(nèi)部定義的躯肌,所以它的公有方法可以訪問私有變量和函數(shù)者春。

(4)增強(qiáng)的模塊模式:

var singleton = function(){
    //私有變量和私有函數(shù)
    var privateVariable = 10;
    function privateFunction(){
        return false;
    }
    //創(chuàng)建對(duì)象,這里CustomType是一種實(shí)例類型清女,我們不需要理會(huì)它的具體代碼
    var object = new CustomType();
    //公有屬性和方法
    object.publicProperty = true;
    object.publicMethod = function(){
        privateVariable++;
        return privateFunction();
    }
    //返回對(duì)象
    return object;
}();

增強(qiáng)的模塊模式钱烟,不是直接返回對(duì)象字面量,而是創(chuàng)建一個(gè)對(duì)象實(shí)例,增加屬性和方法后返回拴袭。
這種模式读第,適合單例是某種類型實(shí)例的情況。上面的代碼拥刻,object是CustomType的實(shí)例怜瞒,匿名函數(shù)返回object對(duì)象,并賦值給singleton變量般哼。所以吴汪,該單例singleton是CustomType的實(shí)例。

代碼來源:
《JavaScript 高級(jí)程序設(shè)計(jì)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蒸眠,一起剝皮案震驚了整個(gè)濱河市漾橙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌楞卡,老刑警劉巖霜运,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蒋腮,居然都是意外死亡淘捡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門池摧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來焦除,“玉大人,你說我怎么就攤上這事作彤√呔” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵宦棺,是天一觀的道長(zhǎng)瓣距。 經(jīng)常有香客問我,道長(zhǎng)代咸,這世上最難降的妖魔是什么蹈丸? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮呐芥,結(jié)果婚禮上逻杖,老公的妹妹穿的比我還像新娘。我一直安慰自己思瘟,他們只是感情好荸百,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著滨攻,像睡著了一般够话。 火紅的嫁衣襯著肌膚如雪蓝翰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天女嘲,我揣著相機(jī)與錄音畜份,去河邊找鬼。 笑死欣尼,一個(gè)胖子當(dāng)著我的面吹牛爆雹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播愕鼓,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼钙态,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了菇晃?” 一聲冷哼從身側(cè)響起驯绎,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谋旦,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屈尼,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡册着,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脾歧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甲捏。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鞭执,靈堂內(nèi)的尸體忽然破棺而出司顿,到底是詐尸還是另有隱情,我是刑警寧澤兄纺,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布大溜,位于F島的核電站,受9級(jí)特大地震影響估脆,放射性物質(zhì)發(fā)生泄漏钦奋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一疙赠、第九天 我趴在偏房一處隱蔽的房頂上張望付材。 院中可真熱鬧,春花似錦圃阳、人聲如沸厌衔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽富寿。三九已至睬隶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間作喘,已是汗流浹背理疙。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泞坦,地道東北人窖贤。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像贰锁,于是被迫代替她去往敵國和親赃梧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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

  • 最近學(xué)這塊知識(shí)學(xué)得有些吃力豌熄。還有很多遺漏的地方授嘀,只能以后多看些書來彌補(bǔ)了。 第7章 函數(shù)表達(dá)式 函數(shù)定義的兩種方式...
    丨ouo丨閱讀 371評(píng)論 0 1
  • 定義函數(shù)的方式有兩種:函數(shù)聲明和函數(shù)表達(dá)式锣险。 函數(shù)聲明的一個(gè)重要特征就是函數(shù)聲明提升蹄皱,意思是在執(zhí)行代碼前會(huì)先讀取函...
    oWSQo閱讀 668評(píng)論 0 0
  • 繼承 一、混入式繼承 二芯肤、原型繼承 利用原型中的成員可以被和其相關(guān)的對(duì)象共享這一特性巷折,可以實(shí)現(xiàn)繼承,這種實(shí)現(xiàn)繼承的...
    magic_pill閱讀 1,064評(píng)論 0 3
  • 看你笑 再累的我 也不會(huì)流淚 看你哭 再冷的我 也都會(huì)慌張 你 就是我 全部的人生
    孤獨(dú)且燦爛閱讀 128評(píng)論 0 0
  • 圖片發(fā)自簡(jiǎn)書App 奇葩年年有署拟,今年特別多。自從進(jìn)了大學(xué)歌豺,有些人的為人處事一次又一次的刷新了我的三觀推穷。 1/ 朋友...
    范慢小透閱讀 357評(píng)論 0 0