我的JS筆記 -- 閉包


閉包是JS中一個(gè)很重要的概念,閉包其實(shí)是基于詞法作用域規(guī)則實(shí)現(xiàn)的,詞法作用域規(guī)則會(huì)使函數(shù)在查找變量時(shí)從函數(shù)內(nèi)部再到函數(shù)定義時(shí)的作用域,而不是從函數(shù)內(nèi)部到函數(shù)使用時(shí)的作用域。所以無論函數(shù)在哪里被調(diào)用,也無論它如何被調(diào)用,它的詞法作用域都只由函數(shù)被聲明時(shí)所處的位置決定。

基于這個(gè)規(guī)則,那么函數(shù)在當(dāng)前詞法作用域之外執(zhí)行,也可以記住并訪問函數(shù)聲明時(shí)所在的詞法作用域,這時(shí)就產(chǎn)生了閉包。

高程定義閉包:閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。

function f1() {
    var a = 1; // 3.調(diào)用的函數(shù)內(nèi)部使用了父級(jí)作用域的內(nèi)部變量
    function f2() { // 1.調(diào)用的函數(shù)是父級(jí)作用域內(nèi)部聲明的
        console.log(a);
    }
    return f2;
}
var f3 = f1(); // 2.調(diào)用的函數(shù)是在父級(jí)作用域之外進(jìn)行調(diào)用,foo()執(zhí)行后將bar 函數(shù)本身當(dāng)作一個(gè)值類型進(jìn)行傳遞給baz。
f3(); // 這就是閉包的效果。執(zhí)行之后,輸出f1中的a,因?yàn)椴徽摵螘r(shí)何處調(diào)用f2都能訪問f1的變量所以f1不會(huì)被回收

閉包產(chǎn)生條件

通過以上代碼,我們可以得到閉包產(chǎn)生的條件:

  1. 調(diào)用的函數(shù)是父級(jí)作用域內(nèi)部聲明的;
  2. 調(diào)用的函數(shù)是在父級(jí)作用域之外進(jìn)行調(diào)用;
  3. 調(diào)用的函數(shù)內(nèi)部使用了父級(jí)作用域的內(nèi)部變量;

總結(jié)便是:無論使用何種方式對(duì)函數(shù)類型的值進(jìn)行傳遞,當(dāng)函數(shù)在別處被調(diào)用時(shí)都可以觀察到閉包。

// 無論通過何種手段將內(nèi)部函數(shù)傳遞到所在的詞法作用域以外, 它都會(huì)持有對(duì)原始定義作用域的引用,無論在何處執(zhí)行這個(gè)函數(shù)都會(huì)使用閉包。

function foo1() {
    var a = 1;
    function baz1() {
        console.log(a); // 1
    }
    bar1(baz1); // baz1被作為參數(shù)傳遞到外部函數(shù)bar1中
}
function bar1(fn) {
    fn(); // 這就是閉包!
}
foo1();

var fn2;
function foo2() {
    var a = 2;
    function baz2() {
        console.log(a);
    }
    fn2 = baz2; // 將 baz2分配給全局變量,也相當(dāng)于傳遞到外部
}
function bar2() {
    fn2(); // 這就是閉包!
}
foo2();
bar2(); // 2

// 主要看看是否是外部調(diào)用。因?yàn)橛脩酎c(diǎn)擊時(shí)觸發(fā)事件,不是在foo3中內(nèi)部調(diào)用的。
var foo3 = function () {
    var btn = document.querySelector("#myBtn");
    var a = 3;
    btn.onclick = function () {
        alert(a);
    }
}
foo3();

下面是一個(gè)關(guān)于閉包的金典例子:

for (var i = 1; i <= 5; i++) { // 只有一個(gè)全局作用域,運(yùn)行timer是尋找變量i只有全局的i = 6
    setTimeout(function timer() {
        console.log(i); // 運(yùn)行時(shí)會(huì)以每秒一次的頻率輸出五次 6
    }, i * 1000);
}
// 首先解釋6是從哪里來的。 這個(gè)循環(huán)的終止條件是i不再<=5。 條件首次成立時(shí)i的值是6。因此,輸出顯示的是循環(huán)結(jié)束時(shí)i的最終值。延遲函數(shù)的回調(diào)會(huì)在循環(huán)結(jié)束時(shí)才執(zhí)行。事實(shí)上,當(dāng)定時(shí)器運(yùn)行時(shí)即使每個(gè)迭代中執(zhí)行的是setTimeout(.., 0),所有的回調(diào)函數(shù)依然是在循環(huán)結(jié)束后才會(huì)被執(zhí)行,因此會(huì)每次輸出一個(gè)6出來。

for (var i = 1; i <= 5; i++) { 
    // 每次循環(huán)創(chuàng)建一個(gè)立即函數(shù),產(chǎn)生一個(gè)新的作用域
    (function (j) { // 利用立即函數(shù),每次循環(huán)創(chuàng)建單獨(dú)的函數(shù)作用域并捕獲每次循環(huán)的i作為參數(shù)傳入,timer函數(shù)是一個(gè)閉包,它在立即函數(shù)中聲明,在setTimeOut回調(diào)使用,它會(huì)保留傳入的參數(shù)i的值,當(dāng)延遲函數(shù)在作用域之外調(diào)用時(shí),仍能訪問到i
        setTimeout(function timer() {
            console.log(j); // 能夠正常輸出1, 2, 3, 4, 5
        }, j * 1000);
    })(i);
}

閉包作用

閉包的最大用處有兩個(gè),一個(gè)是可以讀取函數(shù)內(nèi)部的變量,另一個(gè)就是讓這些變量始終保持在內(nèi)存中。函數(shù)的執(zhí)行上下文,在執(zhí)行完畢之后,生命周期結(jié)束,那么該函數(shù)的執(zhí)行上下文就會(huì)失去引用。其占用的內(nèi)存空間很快就會(huì)被垃圾回收器釋放??墒情]包的存在,會(huì)阻止這一過程雖然例子中的閉包被保存在了全局變量中,但是閉包的作用域鏈并不會(huì)發(fā)生任何改變。在閉包中,能訪問到的變量,仍然是作用域鏈上能夠查詢到的變量即閉包可以使得它誕生環(huán)境一直存在。請(qǐng)看下面的例子,閉包使得內(nèi)部變量記住上一次調(diào)用時(shí)的運(yùn)算結(jié)果:

function addNum(num) {
    return function () {
        return num++;
    };
}
var add = addNum(1);
add() // 1
add() // 2
add() // 3
// 上面代碼中,num是函數(shù)addNum的內(nèi)部變量。通過閉包,start的狀態(tài)被保留了,每一次調(diào)用都是在上一次調(diào)用的基礎(chǔ)上進(jìn)行計(jì)算。從中可以看到,閉包add使得函數(shù)addNum的內(nèi)部環(huán)境,一直存在。所以,閉包可以看作是函數(shù)內(nèi)部作用域的一個(gè)接口

更多文章在 這里 ,覺得不錯(cuò)希望點(diǎn)個(gè) star

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

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

  • 談起閉包,它可是JavaScript兩個(gè)核心技術(shù)之一(異步和閉包),在面試以及實(shí)際應(yīng)用當(dāng)中,我們都離不開它們,甚至...
    sponing閱讀 773評(píng)論 0 7
  • 閉包(closure)是Javascript語言的一個(gè)難點(diǎn),也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)。 一、變量...
    zock閱讀 1,117評(píng)論 2 6
  • 閉包: 官方”的解釋是:閉包是一個(gè)擁有許多變量和綁定了這些變量的環(huán)境的表達(dá)式(通常是一個(gè)函數(shù)),因而這些變量也是該...
    小裁縫sun閱讀 700評(píng)論 0 5
  • 究竟什么是閉包? 閉包在什么場(chǎng)景下使用? 寫前端程序需要用到閉包嗎?我用jQuery也能寫的好好滴呀? 對(duì)于初學(xué)者...
    佩吉秋閱讀 17,905評(píng)論 9 111
  • 系統(tǒng)預(yù)設(shè)風(fēng)險(xiǎn),根據(jù)系統(tǒng)返回的常用數(shù)據(jù),可以右滑添加,不可刪除: 自定義預(yù)設(shè)風(fēng)險(xiǎn),根據(jù)自己需求,可以右上角添加,左滑...
    仝小六閱讀 253評(píng)論 0 1

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