JavaScript閉包

閉包

  • 使用chrome調(diào)試查看
  • 閉包是有權(quán)訪問另一個函數(shù)作用域的變量的函數(shù).
    簡而言之,這些函數(shù)表達式定義在另一個函數(shù)的函數(shù)體內(nèi),它可以訪問它們所在的外部函數(shù)中聲明的所有局部變量、參數(shù)和聲明的其他內(nèi)部函數(shù)。當其中一個這樣的內(nèi)部函數(shù)在包含它們的外部函數(shù)之外被調(diào)用時,就會形成閉包,例如:
function fn1() {
    var num = 0
    function fn2() { //fn1函數(shù)內(nèi)嵌套著內(nèi)部函數(shù)fn2
        num++
        console.log(num)
    }
    return fn2 //將fn2返回
}

var result = fn1() //相當于result = function fn2() {num++;console.log(num);}
result() //1

在上面的例子中fn2就是閉包。

產(chǎn)生閉包的條件

  • 函數(shù)嵌套
  • 內(nèi)部函數(shù)引用了外部函數(shù)的數(shù)據(jù)(變量/函數(shù))
  • 執(zhí)行外函數(shù)

閉包的生命周期

  • 產(chǎn)生: 在嵌套內(nèi)部函數(shù)定義執(zhí)行完時就產(chǎn)生了(不是調(diào)用)
  • 死亡: 在嵌套的內(nèi)部函數(shù)成為垃圾對象時

閉包的用途

  • 可以讀取函數(shù)內(nèi)部的變量。

    • 根據(jù)作用域鏈定義可知子對象會一級一級地向上尋找所有父對象的變量,所以父對象的所有變量,對子對象都是可見的。上面的例子中,fn2函數(shù)就可以訪問fn1函數(shù)內(nèi)部的局部變量num
    • 平時我們在函數(shù)中的返回值經(jīng)常是一個值,既然fn2函數(shù)能夠獲取fn1內(nèi)部的變量,我們將fn2函數(shù)返回就能在外部獲取num變量了
    • 將fn2返回后,相當于var result = function fn2() {num++;console.log(num);},即是外部的函數(shù)result也能獲取局部變量num了
  • 讓變量的值始終保存在內(nèi)存中。

function fn1() {
    var num = 0
    function fn2() { 
        num++
        console.log('閉包函數(shù)' + num)
    }
    return fn2 
}

var result = fn1() 
result() //閉包函數(shù)1
result() //閉包函數(shù)2

function commonFn() {
    var num = 0
    num++
    console.log('普通函數(shù)' + num)
}
commonFn() //普通函數(shù)1
commonFn() //普通函數(shù)1

從上面的例子中可知,重復(fù)調(diào)用閉包函數(shù)時,每次輸出的num值都會+1,而普通函數(shù)重復(fù)執(zhí)行后num值仍然保持不變,這證明了函數(shù)fn1中的局部變量num一直保存在內(nèi)存中,并沒有在f1調(diào)用后被自動清除。
原因:fn1是fn2的父函數(shù),fn2被賦值給全局變量result,這導致fn2始終在內(nèi)存中,而fn2依賴于fn1,故fn1的變量值一直存在于內(nèi)存中。

閉包的缺點

由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大而會導致內(nèi)存泄漏,所以在調(diào)用結(jié)束之后要將不使用的局部變量刪除掉。

內(nèi)存溢出
  • 一種程序運行出現(xiàn)的錯誤
  • 當程序運行需要的內(nèi)存超過了剩余的內(nèi)存時,就拋出內(nèi)存溢出的錯誤
內(nèi)存泄漏
  • 占用的內(nèi)存沒有及時釋放
  • 內(nèi)存泄漏積累多了就容易導致內(nèi)存溢出
  • 常見的內(nèi)存泄漏
    • 意外的全局變量
    • 沒有及時清理的計時器或回調(diào)函數(shù)
    • 閉包

閉包的this指向問題

let obj = {
    name: 'Joe',
    getName: function() {
        return function() {
            console.log(this.name)
        }
    }
}

obj.getName()() //<empty string>

返回的函數(shù)最終是在全局作用域中,故無法訪問到obj對象中的name屬性。

閉包的例子

  • 創(chuàng)建函數(shù)工廠
function addFunc(x) {
    return function(y) {
        return x + y
    }
}

var add5 = addFunc(5)
var add10 = addFunc(10)

console.log(add5(2)) //7
console.log(add10(2)) //12

以上例子是創(chuàng)建兩個數(shù)值相加的函數(shù)工廠,var add5 = addFunc(5)這行代碼先給x傳值5,且這行代碼相當于var add5 = function(y) {return 5 + y}在打印行再給y傳值2.

  • 返回一個對象的寫法
let counter = function() {
    let currentVal = 0
    function addVal(value) {
        currentVal += value
    }

    return {
        increment: function() {
            addVal(1)
        },
        decrement: function() {
            addVal(-1)
        },
        value: function() {
            return currentVal
        }
    }
}

let counter1 = counter()
let counter2 = counter()

console.log(counter1.value()) //0
counter1.increment()
counter1.increment()
console.log(counter1.value()) //2
counter1.decrement()
console.log(counter1.value()) //1

console.log(counter2.value()) //0

由這個例子可知counter1和counter2是獨立的,互相不影響的,也就是它們的值是私有的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 總括 :這篇文章使用有效的javascript代碼向程序員們解釋了閉包,大牛和功能型程序員請自行忽略。 譯者...
    KX九五閱讀 328評論 0 1
  • 本文摘錄及參考自:1. 學習Javascript閉包(Closure)2. 閉包的秘密3. JavaScript ...
    chenhong_f1e2閱讀 537評論 0 2
  • 老生常談的問題,什么是閉包? 我覺得阮一峰老師說的特別好:閉包簡單來說,就是在函數(shù)內(nèi)部訪問函數(shù)局部變量的內(nèi)部函數(shù)。...
    巨龍在盯著你吶閱讀 136評論 0 0
  • 本篇文章包懂 什么是閉包 總有人看到閉包就頭疼,因為“閉包”二字實在讓人搞不懂它的語義。但閉包其實是個很簡單的概念...
    鶴仔z閱讀 275評論 1 2
  • f2是一個閉包,fn1擁有一個局部變量i,fn1調(diào)用結(jié)束后,其變量i因為被fn2引用,所以并沒有被垃圾回收。 從作...
    YucinChow閱讀 206評論 0 0

友情鏈接更多精彩內(nèi)容