閉包 | 淺談JavaScript閉包問題

WangHuagang發表於2018-04-01

1.前言

閉包這個東西在JavaScript中是一個很強大的東西,但是在初學的階段總是被其概念繞暈,搞不清楚到底什麼是閉包,感覺很高深。作者在剛學的時候也有一點懵圈,多看幾次就會有自己的理解。任何東西都沒有唯一的標準,只要適合自己,自己能夠理解的就是正確的。由於作者水平有限,將自己的一些見解拿出來,希望大家能夠提出寶貴的意見。

2.作用域

在正式講閉包之前,我們來簡單的說一說作用域這個東西。這個很有助於大家理解後面的閉包。
什麼是域?簡單的說就是一個被圈起來的地方,也就是變數能夠訪問的一個範圍。
眾所周知,變數的作用域分為全域性變數和區域性變數。定義在函式外部的稱為全域性變數,在函式內部的稱為區域性變數。這裡順帶提一下,變數提升這個玩意兒,也就是和“先宣告後使用差不多的道理”,後面我將會舉一個例子來說明。
舉個例子,全域性變數和區域性變數:

1
2
3
4
5
6
7
8
9
10
11
12
複製程式碼
var a='wang';
function fun(){
    var b='huagang';
    console.log('這是在函式內部的輸出');
    console.log(a);
    console.log(b);
}
fun();//呼叫函式

console.log("這是在函式外部的輸出");
console.log(a);
console.log(b);
複製程式碼

執行結果:
執行結果
最後一個輸出b的時候丟擲了一個未定義異常,由此可見,全域性變數(a)在函式內部和外部都是能訪問的,但是區域性變數不是這樣的,在函式作用域外是不能訪問到函式內部的變數(b)的。這裡順帶講一下【變數提升】吧,簡單的舉一個例子。

1
2
3
4
複製程式碼
var a;
console.log(a);
a='wang';
console.log(a)
複製程式碼

執行結果
根據輸出情況可以看出,第一次輸出a並沒有報異常,而是undefined。第二次便能輸出a的值。這就是變數提升的特點,在變數還沒賦值前就拿來使用了。

3.巢狀函式的作用域

巢狀函式,顧名思義就是在函式的內部再寫一個或多個函式。下面舉一個例子來講解一下巢狀函式的作用域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
複製程式碼
function funA(c){
    var a='wang';
    function funB(){
        var b='gang';
        console.log("在函式B中的輸出:")
        console.log(a);
        console.log(c);
        console.log(b);
    }
    funB();
    console.log("在函式A中的輸出:")
    console.log(a);
    console.log(c);
    console.log(b);
}
funA('hua');//呼叫函式A並傳參
複製程式碼

執行結果
函式B就是巢狀在函式A中的巢狀函式,它可以繼承函式A的變數和引數,但是B中的變數A是不能訪問的,就好像B給自己的門上了鎖,“只進不出”,我可以拿你的東西,但是你不可以拿我的東西。B這樣就形成了一個自己獨有的封閉空間,這就是一個閉包。
從不同的角度來看:從語法結構上看,函式A包含函式B;從作用域來看,函式B包含函式A,也就是說B能訪問的空間比A大。
因此可以想象得出,假如B函式中還有一個巢狀函式X,那麼這個X函式也是一個閉包,作用域包含B和A。這樣下去就形成了一個作用域鏈。

4.閉包

相信通過上面的講解,大家已經懂得了什麼是閉包,現在再講講一些關於閉包的特點或者特性吧。

儲存變數

什麼是儲存變數?加入B這個閉包需要兩個變數才能執行,但是剛開始只傳了一個引數進去,所以這時候閉包就會把這個變數的值進行儲存,等待第二個變數傳入,而不是丟棄這個變數的值。下面舉一個例子進行說明吧。

1
2
3
4
5
6
7
8
9
複製程式碼
function funA(a){
    function funB(b){
        return a + b;
    }
    return funB;//呼叫函式B的引用
}
var x = funA(2);
var sum = x(3);
console.log(sum);//輸出結果 5
複製程式碼

執行結果
當函式A傳進引數a=2時,這時閉包B就將a進行儲存,等到再傳b=3時再進行計算。這就是閉包的儲存變數。

5.為什麼要使用閉包

使用閉包的最大的好處——避免變數的汙染。也就是說你在閉包中宣告的變數不會影響在其他地方也使用這個變數名稱,因為閉包將這個變數鎖在自己的門裡面保護起來了,外部是無法修改的。

1
2
3
4
5
6
7
8
9
10
11
複製程式碼
function funA(){
    function funB(b){
        var a = 'wang';//內部變數 c
        return a+b ;
    }
    return funB;
}
var a = 'hua';//外部變數x,是不能改變閉包B中的變數a的
var sum = funA()(a);
console.log(a);
console.log(sum);
複製程式碼

執行結果
可以看出,外部變數是不能修改閉包中的變數a的值的,從而保護了a的值,使其不會受到汙染。

閉包的分享就講到這裡啦,我相信大家能夠對閉包能夠有一定的瞭解。當然,由於作者水平有限,這只是個人的見解,有誤的地方還希望多多包涵,或者留言告知我。


我的部落格地址:【咕嚕先森的部落格

個人微信公眾號:【咕嚕有得聊】,歡迎關注,一起學習!

閉包 | 淺談JavaScript閉包問題


相關文章