閉包一詞在javascript中很是有名,對於前端來講理解它相當重要,不少高級應用都要依靠閉包實現。掌握了它,對咱們的js寫代碼水平會有很大幫助,所以成爲許多公司前端的面試題之一,用來測試應聘者的js水平,可見其重要性,因此做爲一個前端,理解閉包是必須的!沒有任何理由說不懂!javascript
理解閉包,首先必須理解變量做用域和做用域鏈。在JavaScript中,JavaScript有兩種做用域:全局做用域和函數做用域。函數內部能夠直接讀取全局變量。函數子做用域能夠訪問父做用域的變量 。前端
var n = 1;
function f1() {
console.log(n);
}
f1() // 1
複製代碼
上面代碼中,函數f1能夠直接訪問全局變量n, 可是在函數外部沒法直接讀取函數內部的變量,java
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就是閉包,。閉包是指有權訪問另外一個函數做用域中的變量的函數。在這裏要注意的一點:因爲一般閉包都是匿名函數,因此給人形成錯覺,只有匿名函數才能做爲閉包,其實,命名、匿名函數都是能夠做爲閉包函數的,只不過一般閉包都是做爲返回值,自身不多被調用,因此也就沒了命名的必要,而命名函數基本上都是要調用的。bash
另外看了網上一些文章,是經過做用域的提高來解釋的, 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的垃圾回收機制引發的,目前已經修復這個問題,