閉包一詞在javascript中非常有名,對於前端來說理解它至關重要,很多高階應用都要依靠閉包實現。掌握了它,對我們的js寫程式碼水平會有很大幫助,因此成為許多公司前端的面試題之一,用來測試應聘者的js水平,可見其重要性,所以作為一個前端,理解閉包是必須的!沒有任何理由說不懂!
理解閉包,首先必須理解變數作用域和作用域鏈。在JavaScript中,JavaScript有兩種作用域:全域性作用域和函式作用域。函式內部可以直接讀取全域性變數。函式子作用域可以訪問父作用域的變數 。
var n = 1;
function f1() {
console.log(n);
}
f1() // 1
複製程式碼
上面程式碼中,函式f1可以直接訪問全域性變數n, 但是在函式外部無法直接讀取函式內部的變數,
function f1() {
var n = 1;
}
console.log(n)
//Uncaught ReferenceError: n is not defined 提示n沒有定義
複製程式碼
如果出於種種原因,需要得到函式內的區域性變數。正常情況下,這是辦不到的,只有通過變通方法才能實現。那就是在函式的內部,再定義一個函式。
function f1() {
var n = 1;
function f2() {
  console.log(n); // 1
}
}
複製程式碼
上面程式碼中,函式f2就在函式f1內部,這時f1內部的所有區域性變數,對f2都是可見的。但是反過來就不行,f2內部的區域性變數,對f1就是不可見的。子物件會順著作用域鏈找父物件的變數, 如果想要函式外部訪問函式內部的變數,那麼可以使用return把f2作為返回值,不就可以在發外部訪問到內部的變數了嗎?
function f1() {
var n = 1;
function f2() {
  console.log(n); // 1
}
return f2
}
var result = f1();
result(); // 1
複製程式碼
上面程式碼中,函式f1的返回值就是函式f2,由於f2可以讀取f1的內部變數,所以就可以在外部獲得f1的內部變數了。 f2就是閉包,。閉包是指有權訪問另一個函式作用域中的變數的函式。在這裡要注意的一點:由於通常閉包都是匿名函式,所以給人造成錯覺,只有匿名函式才能作為閉包,其實,命名、匿名函式都是可以作為閉包函式的,只不過通常閉包都是作為返回值,自身很少被呼叫,所以也就沒了命名的必要,而命名函式基本上都是要呼叫的。
另外看了網上一些文章,是通過作用域的提升來解釋的, f1是一級作用域,f2是二級作用域,f1返回f2後,把f2的作用域提升到一級作用域,就可以在外部被全域性呼叫了,這種說法通俗易懂,很好理解,但是
閉包的優點
1.函式內部的定義的變數可以儲存在記憶體中。一般函式執行後,函式內部的變數就會被銷燬,但是由於閉包的存在,該函式內部的變數就不會被銷燬回收,如上述程式碼中,f1呼叫後,閉包f2會呼叫變數n,使得n始終存在記憶體中。 2.避免全域性變數的汙染,全域性變數是可重用但是汙染全域性,區域性變數不會汙染全域性但是不可重用。而閉包就是二者優點的結合, 3.是封裝物件的私有屬性和私有方法。舉例如下:
function Person(name) {
var _age;
function setAge(n) {
_age = n;
}
function getAge() {
return _age;
}
return {
name: name,
getAge: getAge,
setAge: setAge
};
}
var p1 = Person('張三');
p1.setAge(25);
p1.getAge() // 25
該例子來源與阮一峰的部落格函式閉包內容,
複製程式碼
上面程式碼中,函式Person的內部變數_age,通過閉包getAge和setAge,變成了返回物件p1的私有變數。
閉包的缺點
閉包會保留外層函式的內部變數,所以記憶體消耗很大。因此不能濫用閉包,否則會造成網頁的效能問題。
備註:之前閉包在iE瀏覽器上存在記憶體溢位的問題,不過這是由於ie的垃圾回收機制引起的,目前已經修復這個問題,