以下是學習筆記,來自阮一峯筆記
閉包是Javascript的一個難點,很多面試官都會問。。。
爲了理解閉包必須先理解變量的作用域
一:變量的作用域
大體的分兩類
- 全局變量
- 局部變量
Javascript 這個語言的比較特殊的一點是函數可以獲取外部的變量(全局變量),但外面無法獲取函數內部的變量。
下面的函數可以獲取函數外面的變量(局部變量)
var name = "Abd";
function say() {
console.log(name);
}
say(); //abd
但下面的就不行了,也就是外面無法獲取函數內的變量。
function say() {
let name = "Abdcc";
}
say();
console.log(name); //error
另外注意一點是如果不定義let或war,則這個變量會變成全局的。
function say() {
name = "Abdccfaa";
}
say();
console.log(name); //Abdccfaa
這樣就不公平了,我們得想辦法讀取函數內部的變量啊(笑臉.png).
二:從外部讀取內部變量
正常情況下我們做不到,因此我們得想出辦法.我們想到的辦法就是在函數內定義一個返回我們想要的變量的函數來獲取函數內部的變量。
function say() {
let n = 100;
function bark() {
let m=101;
alert(n);
}
bark();
console.log(m); //m is not defined
}
say();
通過以上的函數我們就實現了獲取say函數內部的變量。函數bark可以獲取say函數內的所用變量,但反過來就不行,這是因爲Javascript的語言特色決定的,子對象會一層一層的向上尋找父對象的變量,反着則不成立.(這個就很現實了,感覺閉包的函數就感覺是啃老族,拿父母親的,但不會給他們回報(給他們自己的變量)).
既然我們可以通過函數(say)內部實現一個函數(bark)來獲取say內部的變量,我們就可以通過返回bark來在say外部獲取say內部的變量.
function say() {
let n = 100;
function bark() {
alert(n);
}
return bark;
}
let result = say();
result(); // alerted the n 100
閉包的概念
上一個函數內bark就是閉包,簡單來講閉包就是會獲取其他函數內部的變量的函數.因爲Javascript中,只有函數內部的自函數才能可以讀取局部變量,因此可以把閉包可以理解成定義在一個函數內部的函數,也就是用來跟父函數(在上面的例子的say函數)跟外面溝通的橋樑。
本質上是一座橋樑。
閉包的作用
主要有以下兩處作用:
- 上文提到的返回父函數內部的變量,起橋樑作用。
- 讓這些變量(父親函數內部的變量)的值始終保存在內存中。
通過以下例子來了解第二點作用。
function f1() {
var n = 999;
nAdd = function() {
n += 1;
};
function f2() {
alert(n);
}
return f2;
}
var result = f1();
result(); // 999
nAdd();
result(); // 1000
這段代碼內result是f2的值。f2(閉包)來返回f1內部的變量的值,然後result執行兩次,第一次是999,第二次是1000.這證明了變量n一直保存在內存中,並沒有因爲f1調用後自動清楚。
原因是這樣的,因爲result是全局變量,因此得保存在內存中,因爲result的保存依賴於f2,f2的保存依賴於f1,從而導致n一直保存在內存中。
這段代碼另一個注意的地方是nAdd函數,nAdd是全局變量(沒有加var等),也是個閉包,也是個匿名函數,所以nAdd是一個setter, 對f1的變量n做起改變。
使用閉包注意點
- 閉包會使得函數中的變量都保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁性能問題。
- 閉包在函數外部改變函數內部的變量的值,因此當函數是對象,把閉包當作它的公用方法,把變量當作私有變量,因此不能頻繁改變函數內部的變量的值。