閉包這個概念看上去很深奧,這個詞在離散數學裏面的意思確實比較難於理解。在這裏,咱們先能夠把閉包理解成是一種匿名函數或者匿名類。閉包
1. 什麼是閉包?函數
什麼是閉包?一種正式的解釋是:所謂閉包,指的是一種擁有不少變量而且綁定了這些變量的環境的表達式(一般是一個函數),於是這些變量也是這個表達式的一部分。this
相信不少人都不會理解這個定義,由於他的學術味道太濃了——或許你喜歡從字面的語法上進行分析:首先,它是一個表達式,這個表達式綁定了不少變量以及這些變量的環境。不過這並無什麼意義,這依然不會告訴咱們什麼是閉包。spa
那麼,來看一個例子:對象
function add(a) {
return function(b) {
return a + b;
};
}
var func = add(10);
alert(func(20));ip
我想通過了前面有關函數的描述,這個例子應該很清楚的理解。JavaScript裏面的函數就是對象,他能夠作對象能作的一切事情——咱們首先定義了一個函數add,它接受一個參數,這個函數返回一個匿名函數,這個匿名函數也接受一個參數,而且會返回這個參數同外部函數的那個參數的和。所以在咱們使用的時候,咱們將add返回的匿名函數賦值給func,而後調用func,就返回了這兩個數的和。內存
當咱們建立一個這樣的函數,這個函數內部的一個變量可以在函數外面被引用時,咱們就稱建立了一個閉包。仔細的品味一下:這就是那個閉包的定義。作用域
看看咱們的代碼:首先,它有一個內部變量,就是那個匿名函數;其次,這個函數將匿名函數返回了出去,以便外面的變量能夠引用到內部定義的變量。get
2. 閉包的做用數學
閉包有什麼用呢?或許如今還看不出來,那麼看看這段代碼:
function inc(a) {
var i = 0;
return function() {
return i;
};
}
var num = inc();
alert(num());
原本,這個變量 i 在函數外面是訪問不到的,由於它是 var 定義的,一旦跳出做用域,這個變量就被垃圾回收了,可是,因爲咱們使用了閉包,在外面是可以訪問到這個變量的,所以它並不被垃圾回收!
若是仍是不明白閉包的做用,那麼看一段應該很熟悉的代碼:
function Person() {
var id;
this.getId = function() {
return id;
}
this.setId = function(newId) {
id = newId;
}
}
var p = new Person();
p.setId(1000);
alert(p.getId()); // 1000
alert(p.id); // undefined
咱們定義一個類Person,它有一個id屬性。如今這個屬性的行爲很像是私有變量——只能經過 setter 和 getter 函數訪問到。沒錯,這就是閉包的一個用途:製造類的私有變量!
閉包還有一個做用:在內存中維護一個變量,不讓垃圾回收器回收這個變量。這裏的例子就再也不舉出了。
這裏咱們只是簡單的說了JavaScript的閉包的概念,並無涉及閉包的內存模型等等之類。這是一個至關重要的概念,Java社區中的部分紅員一直對閉包求之不得,C#也已經在最新版本中添加了閉包的概念,只不過在那裏稱爲lambda表達式。