理解Js的閉包

閉包是Js的一個(gè)難點(diǎn),也是它的一個(gè)特色,很多高級應(yīng)用都要靠閉包來實(shí)現(xiàn)。

1.變量的作用域

要理解閉包,首先必須要理解Js特殊的變量作用域。

變量的作用域無非只有2種,全局變量局部變量

JavaScript語言的特殊之處還在于,函數(shù)內(nèi)部可以直接讀取全局變量。


//  函數(shù)內(nèi)部可以直接讀取全局變量

var n=100

function f1(){
    console.log(n)
}


f1()     // 100

上面代碼中,函數(shù)f1的內(nèi)部,是可以直接讀取全局變量n的。


另一方面,函數(shù)外部自然不能讀取函數(shù)內(nèi)部的局部變量。

//  函數(shù)外部不可以讀取函數(shù)內(nèi)部變量



function f1(){
    var n=100
}

f1()

console.log(n)  //  Uncaught ReferenceError: n is not defined


如上面代碼,在函數(shù)f1外部去嘗試打印n的時(shí)候,就報(bào)錯(cuò)。


那么如何從函數(shù)外部去讀取函數(shù)內(nèi)部的變量呢?

出于種種原因,有時(shí)候我們需要從函數(shù)外部去讀取函數(shù)內(nèi)部的變量。 但是前面說過了,正常情況下,這是辦不到的,那么就要使用變通的方法。

那就是在函數(shù)內(nèi)部,再定義一個(gè)函數(shù),并將其作為返回值

//  函數(shù)作為返回值

function f1(){
    var n=100

    return function bar(x){
        if(x>n){
            console.log(n)
        }
    }
}


var f=f1()

f(102)   // 100

上面的代碼中,我就打印出了函數(shù)內(nèi)部變量n


2.閉包的概念

上面代碼中的bar函數(shù),就是閉包。
閉包實(shí)際上就是指閉包函數(shù),它是一個(gè)函數(shù)。

閉包實(shí)際上就是

能夠讀取其它函數(shù)內(nèi)部變量的函數(shù)

切記,閉包是一個(gè)函數(shù)


3.閉包的用途

閉包可以用在很多方面。它最大的用處有2個(gè)。

讀取其它函數(shù)內(nèi)部變量讓這些變量始終保持在內(nèi)存中

請看下面的代碼

//  函數(shù)作為返回值

function f1(){
    var n=100

    nAdd=function(){
        n++
    }

    return function bar(){
        console.log(n)  
    }
}


var f=f1()

f()   // 100


nAdd()

f()  // 101

上面代碼中,f實(shí)際上就是閉包函數(shù)bar
它一共運(yùn)行了2次,第一次的結(jié)果是100,第二次的結(jié)果是101。

這證明了函數(shù)f1中的變量一直保存在內(nèi)存中。并沒有在f1的調(diào)用結(jié)束后被清除

為什么會這樣呢? 原因就在于,f1是bar的父函數(shù),而子函數(shù)bar被賦值給了一個(gè)全新的全局變量f,而且bar的存在依賴于f1,因此f1也始終存在于內(nèi)存中,不會因?yàn)閒1的調(diào)用完成而被垃圾回收機(jī)制銷毀

這段代碼另一個(gè)值得注意的地方就在于nAdd=function(){n++}這段,首先,nAdd前面沒有使用var關(guān)鍵字,、因此nAdd是一個(gè)全局變量,而不是局部變量。 你看nAdd里面也能讀到其它函數(shù)內(nèi)部的變量n,因此nAdd也是一個(gè)閉包函數(shù)。

所以nAdd相當(dāng)于一個(gè)setter,可以在函數(shù)外部對函數(shù)內(nèi)部的變量進(jìn)行操控。


4.使用閉包注意點(diǎn)


  • 由于使用閉包的使用會使得函數(shù)中定義的變量都保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包。否則會造成網(wǎng)頁性能問題。

    解決辦法,是在退出函數(shù)之前,將不使用的局部變量全部刪除。


  • 閉包會在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。 所以如果你把父函數(shù)當(dāng)做對象(object)來使用,把閉包當(dāng)作它的共有方法來使用,把內(nèi)部變量當(dāng)作它的私有屬性來使用,這時(shí)一定要小心,不要隨便改變父函數(shù)內(nèi)部的值。


5.例子


//  函數(shù)作為返回值


var name="The Window"

var object={
    name:"My object",
    getNameFunc:function(){
        console.log(this) // {name:"My object",getNameFunc:f}
        return function(){
            console.log(this)  // Window
            return this.name
        }
    }
}


console.log(object.getNameFunc()())  //  The Window

上面代碼中,this作為對象object的一個(gè)屬性被調(diào)用時(shí),指向是object這個(gè)對象,但是再return一個(gè)函數(shù)的時(shí)候,this的指向就變成了全局window

函數(shù)的作用域是創(chuàng)建的時(shí)候確定的,不是在調(diào)用的時(shí)候,最內(nèi)層那個(gè)函數(shù)創(chuàng)建的時(shí)候就是全局。 所以this指的是全局window


6. 請看下面這段代碼

//  函數(shù)作為返回值


function a(){
    var i=0;

    return b=()=>{
        console.log(++i)
    }
}


var c=a()

c()   //  1

上面代碼有2個(gè)特點(diǎn):

1.函數(shù)b嵌套在函數(shù)a內(nèi)

2.函數(shù)a返回函數(shù)b

引用關(guān)系如圖

閉包

這樣在執(zhí)行完 var c=a()后,變量c實(shí)際上是指向了函數(shù)b,再執(zhí)行c()就會打印i的值。

上面的代碼實(shí)際上就創(chuàng)建了一個(gè)閉包。

因?yàn)楹瘮?shù)a外的變量c引用了函數(shù)a內(nèi)部的函數(shù)b

就是說,

當(dāng)函數(shù)a的內(nèi)部函數(shù)b被函數(shù)a外的一個(gè)變量引用的時(shí)候,就創(chuàng)建了一個(gè)閉包

所謂“閉包”,就是在構(gòu)造函數(shù)體內(nèi)定義另外的函數(shù)作為目標(biāo)對象的方法函數(shù),而這個(gè)對象的方法函數(shù)反過來引用外層函數(shù)體中的臨時(shí)變量。這使得只要目標(biāo) 對象在生存期內(nèi)始終能保持其方法,就能間接保持原構(gòu)造函數(shù)體當(dāng)時(shí)用到的臨時(shí)變量值。盡管最開始的構(gòu)造函數(shù)調(diào)用已經(jīng)結(jié)束,臨時(shí)變量的名稱也都消失了,但在目 標(biāo)對象的方法內(nèi)卻始終能引用到該變量的值,而且該值只能通這種方法來訪問。即使再次調(diào)用相同的構(gòu)造函數(shù),但只會生成新對象和方法,新的臨時(shí)變量只是對應(yīng)新 的值,和上次那次調(diào)用的是各自獨(dú)立的。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,743評論 2 370

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