JavaScript閉包

閉包

閉包就是能夠?qū)⒑瘮?shù)內(nèi)部的變量與外部進(jìn)行通信的一座橋梁怔鳖。
function f() {
      var n = 100
      console.log(n) // 100
  }

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

  f()

我們定義了一個(gè)函數(shù),函數(shù)在下面f()被調(diào)用,函數(shù)就會(huì)從上往下執(zhí)行读第,執(zhí)行完之后函數(shù)內(nèi)部的聲明的變量都會(huì)被清空掉,函數(shù)內(nèi)部的變量是對(duì)外界不開放的

  • 假如我們想要在另外一個(gè)函數(shù)里使用這個(gè)變量n我們有哪些方法哪拥刻?
function f() {
    var n = 100
    console.log(n) //100
    function increment () {
        n = n + 1;
        console.log(n) //101
    }
    increment()
}

f()

我們?cè)趂()函數(shù)里定義了一個(gè)increment()函數(shù),里面使用了變量n,這次并沒有報(bào)錯(cuò)父泳。這就是js里鏈?zhǔn)阶饔糜虬愫撸M(jìn)行冒泡似的查找變量聲明。

上面的例子惠窄,我們看到了一個(gè)函數(shù)體內(nèi)定義了另外一個(gè)函數(shù)蒸眠。我們知道js中函數(shù)是可以作為變量傳遞的,那么我們修改一下上面的例子

function f(increment) {
    var n = 100
    console.log(n) // 100
    increment(n) //調(diào)用
}

f(function(n) { 
    console.log(n + 1) // 101
})

發(fā)生了什么杆融,我們將increment函數(shù)作為變量傳給了f函數(shù)楞卡,大家看完這個(gè)例子是不是感覺有點(diǎn)熟悉的感覺。我們加入一些打印看下執(zhí)行順序

function f(increment) {
    console.log("f函數(shù)開始")
    var n = 100
    console.log(n) // 100
    increment(n) //調(diào)用
    console.log("f函數(shù)結(jié)束")
}

f(function(n) { 
    console.log(n + 1) // 101
    console.log("increment函數(shù)")
})
//打印順序
// f函數(shù)開始
// 100
// 101
// increment函數(shù)
// f函數(shù)結(jié)束

我們看到在f函數(shù)里是從上下到下一次執(zhí)行的,其實(shí)上面的例子和下面的事等價(jià)的

function f() {
    var increment = function(n) {
        console.log(n + 1)
    }

    var n = 100;
    console.log(n) // 100
    increment(n) // 101
}

f()

好了蒋腮,說了這么多閉包到底可以干啥類淘捡。就如我們上面所說的,可以捕獲函數(shù)里的變量池摧,比如我們函數(shù)體內(nèi)有一個(gè)異步操作焦除,我們想在這個(gè)函數(shù)執(zhí)行完這些異步操作之后
就可以把這些數(shù)據(jù)傳出去供別人使用了。

function getData(successCallBack) {
    console.log("getData函數(shù)開始執(zhí)行")
    setTimeout(function() {
        console.log("執(zhí)行回調(diào)")
        successCallBack("這些是數(shù)據(jù)")
    }, 3000);
    console.log("getData函數(shù)體結(jié)束")
}

getData((data) => {
    console.log(data)
})

//執(zhí)行結(jié)果如下

// getData函數(shù)開始執(zhí)行
// getData函數(shù)體結(jié)束
// 執(zhí)行回調(diào)
// 這些是數(shù)據(jù)

閉包會(huì)造成內(nèi)存泄漏作彤,下面我們玩?zhèn)€游戲1,1,2,3,5,8,13,21,34...求第n位是多少膘魄!一個(gè)經(jīng)典的算法題

  • 使用遞歸
var count = 0

function fib (num) {
    console.log(count ++) 
    if (num <= 0) {
        return 0
    }
    if (num === 1) {
        return 1
    }
    return fib(num - 2) + fib(num - 1)
}

console.log(fib(7)) //8

fib(6) = fib(5) + fib(4), fib(5) = fib(4) + fib(3) ...... f(2) = f(1) + f(0),一直到最后我們才找到f(0) = 0, f(1) = 1,那么此時(shí)函數(shù)f(2)才會(huì)被釋放掉,然后f(3)回后f(3)才會(huì)被釋放掉竭讳。创葡。。,
所以如果num數(shù)夠大的話那么內(nèi)存里存儲(chǔ)的這些函數(shù)也會(huì)增多绢慢。大家可以測(cè)試一下我測(cè)的當(dāng)num = 45時(shí)就會(huì)執(zhí)行大概18s,這個(gè)數(shù)字會(huì)根據(jù)有所不同灿渴,根據(jù)電腦性能,反正是很耗時(shí)的呐芥。

var count = 0

function fib (index , left, right) { //使用right作為變量逻杖,存儲(chǔ)最后這個(gè)值
    console.log(count ++) 
    if (index === 1) {
        return right
    }
    return fib(index - 1, right, left + right)
}

console.log(fib(7,0,1)) // 8
  • 如何不在函數(shù)里寫控制語句,完成一個(gè)循環(huán)
function circle(index) {
    if (index === 0) {
        return 
    }
    console.log(index)
    return circle(index - 1)
}
circle(5) //5 4 3 2 1
  • 閉包是如何保存上下文的
function print() {
    console.log("print開始執(zhí)行")    
    for (var index = 0; index < 5; index++) {
        console.log("for begin")        
        setTimeout(function() {
            console.log("callback begin")            
            console.log(index) // 5,5,5,5,5
        }, 1000);
    }
    console.log("print執(zhí)行結(jié)束")    
}

print()

//執(zhí)行順序
// print開始執(zhí)行
// for begin
// for begin
// for begin
// for begin
// for begin
// print執(zhí)行結(jié)束
// callback begin
// 5
// callback begin
// 5
// callback begin
// 5
// callback begin
// 5
// callback begin
// 5

打印了五個(gè)五為什么思瘟?在for語句里setTimeout是個(gè)異步的函數(shù)荸百,在for執(zhí)行完之后index已經(jīng)是五了,然后在打印滨攻,當(dāng)然都是5了

  • 上面的例子相當(dāng)于下面的
function print() {
    console.log("print開始執(zhí)行")    
    for (var index = 0; index < 5; index++) {
        console.log("for begin")
        var callBack =  function() { 
            console.log("callback begin")            
            console.log(index) // 5,5,5,5,5
        }       
        setTimeout(callBack, 1000);
    }
    console.log("print執(zhí)行結(jié)束")    
}

print()
  • 使用閉包留index的值
function print() {
    console.log("print開始執(zhí)行")    
    for (var index = 0; index < 5; index++) {
        console.log("for begin")
        var callBack =  function(index) { 
            console.log("callback begin")            
            console.log(index) // 5,5,5,5,5
        }       
        setTimeout(callBack(index), 1000); //callBack保留index的值
    }
    console.log("print執(zhí)行結(jié)束")    
}

print()
//打印順序

// print開始執(zhí)行
// for begin
// callback begin
// 0
// for begin
// callback begin
// 1
// for begin
// callback begin
// 2
// for begin
// callback begin
// 3
// for begin
// callback begin
// 4
// print執(zhí)行結(jié)束
  • 最終就變成了我們經(jīng)常寫的下面這部分
function print(callBack) {
    console.log("print開始執(zhí)行")
    for (var index = 0; index < 5; index++) {
        console.log("for begin")
        setTimeout(callBack(index), 1000);
    }
    console.log("print執(zhí)行結(jié)束")
}

var callBack = (index) => {
    console.log("callback begin")
    console.log(index)
}

print(callBack)

//執(zhí)行順序
// print開始執(zhí)行
// for begin
// callback begin
// 0
// for begin
// callback begin
// 1
// for begin
// callback begin
// 2
// for begin
// callback begin
// 3
// for begin
// callback begin
// 4
// print執(zhí)行結(jié)束
  • 在一個(gè)我們經(jīng)常見的就是函數(shù)作為返回值進(jìn)行返回
function increment(x) {
    return function (y) {
        return x + y
    }
}

var incre10 = increment(10) //相當(dāng)于 function(y) {return 10 + y}
console.log(incre10(8)) // 18

//increment相當(dāng)于一個(gè)構(gòu)造器可以創(chuàng)建函數(shù)
  • 考慮一下例子
var object = {
    name: 'Bill',
    getName: function() {
        return function() {
            return this.name
        }
    }
}
console.log(object.getName()()) //undefined

我們看到getName的上下文是object 那么getName的中使用this的話應(yīng)該是指向object
object.getName()()此時(shí)的上下文是global的因?yàn)槲以趎ode環(huán)境下執(zhí)行的如果在網(wǎng)頁里應(yīng)該是window

global.name = 'Jason'

var object = {
    name: 'Bill',
    getName: function() {
        return function() {
            return this.name
        }
    }
}
console.log(object.getName()()) //Jason
  • 還有一種方法
var object = {
    name: 'Bill',
    getName: function() {
        var that = this //getName的上下文環(huán)境是object
        return function() {
            return that.name
        }
    }
}
console.log(object.getName()()) //Jason

所以使用閉包的時(shí)候要注意上下文環(huán)境問題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末够话,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子光绕,更是在濱河造成了極大的恐慌女嘲,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诞帐,死亡現(xiàn)場(chǎng)離奇詭異欣尼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)停蕉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門愕鼓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人慧起,你說我怎么就攤上這事菇晃。” “怎么了蚓挤?”我有些...
    開封第一講書人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵磺送,是天一觀的道長(zhǎng)驻子。 經(jīng)常有香客問我,道長(zhǎng)估灿,這世上最難降的妖魔是什么崇呵? 我笑而不...
    開封第一講書人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任设拟,我火速辦了婚禮炫七,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘霸褒。我一直安慰自己司顿,他們只是感情好芒粹,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著大溜,像睡著了一般化漆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钦奋,一...
    開封第一講書人閱讀 52,793評(píng)論 1 314
  • 那天座云,我揣著相機(jī)與錄音,去河邊找鬼付材。 笑死朦拖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的厌衔。 我是一名探鬼主播璧帝,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼富寿!你這毒婦竟也來了睬隶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤页徐,失蹤者是張志新(化名)和其女友劉穎苏潜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體变勇,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恤左,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搀绣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片飞袋。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖豌熄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情物咳,我是刑警寧澤锣险,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布蹄皱,位于F島的核電站,受9級(jí)特大地震影響芯肤,放射性物質(zhì)發(fā)生泄漏巷折。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一崖咨、第九天 我趴在偏房一處隱蔽的房頂上張望锻拘。 院中可真熱鬧,春花似錦击蹲、人聲如沸署拟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽推穷。三九已至,卻和暖如春类咧,著一層夾襖步出監(jiān)牢的瞬間馒铃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工痕惋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留区宇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓值戳,卻偏偏與公主長(zhǎng)得像议谷,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子述寡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

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

  • 閉包(closure)是Javascript語言的一個(gè)難點(diǎn)柿隙,也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)鲫凶。 一禀崖、變量...
    zouCode閱讀 1,275評(píng)論 0 13
  • 閉包(Closure)概念 在A函數(shù)中定義了一個(gè)B函數(shù),在B函數(shù)中使用了A函數(shù)中的變量螟炫,就會(huì)產(chǎn)生閉包波附,其中B就是一...
    MonkeyDwwl閱讀 463評(píng)論 2 5
  • 前言 總括 :這篇文章使用有效的javascript代碼向程序員們解釋了閉包,大牛和功能型程序員請(qǐng)自行忽略昼钻。 譯者...
    KX九五閱讀 279評(píng)論 0 1
  • 如果要了解閉包掸屡,我們需要先了解閉包的由來,閉包的產(chǎn)生然评,源于JS的詞法作用域 詞法作用域 作用域是指一個(gè) 變量能夠訪...
    羊烊羴閱讀 240評(píng)論 0 2
  • 我愛你― 以一種暗戀的姿態(tài) 在你棉白的懷抱 將愛低頭深埋 根植在土地里 保持與你的距離 同時(shí)與你親密 我愛你 你的...
    簡(jiǎn)凈有致閱讀 348評(píng)論 2 8