看以下例子,注意內部函數form
是如何引用定義定義在外部函數formFamily
內的requiredWho變量的?咱們須要知道JavaScript容許你引用當前函數之外定義的變量。javascript
function formFamily (){
var requiredWho = "Father and Mother ";
function form (who){
return requiredWho + "need " + who ;
}
return form ("YingBao and MaoTan");
}
formFamily(); // "Father and Mother need YingBao and MaoTan"複製代碼
例子演進,這個例子和上面幾乎徹底相同,惟一區別是,不是在外部函數formFamily
中當即調用f("YingBao and MaoTan"),
而是返回form
函數自己。所以f的值爲內部的函數form
,調用f實際是調用form函數。但即便formFamily
函數已經返回,form
仍能記住requiredWho的值。java
這裏咱們須要知道,即便外部函數返回,當前函數仍然能夠引用在外部函數所定義的變量。這就意味着,你能夠返回一個內部函數,並在稍後調用它。web
function formFamily (){
var requiredWho = "Father and Mother ";
function form (who){
return requiredWho + "need " + who ;
}
return form;
}
var f = formFamily();
f("YingBao"); // "Father and Mother need YingBao"
f("MaoTan"); // "Father and Mother need MaoTan"
f("YingBao and MaoTan"); // "Father and Mother need YingBao and MaoTan"複製代碼
這是如何工做的?編程
在外部函數 formFamily
中定義內部函數
form
,而 form
又引用了 formFamily
做用域內的倆個變量requiredWho
和who
。每當form
函數被調用時,其代碼都能引用到這倆個變量,由於該閉包存儲了這個倆個變量。form
函數就是一個閉包。bash
在外部函數 formFamily 以外使用內部 form 函數,而且引用外部函數的變量,則造成了閉包。閉包
JavaScript的函數值包含了比調用它們時執行所須要的代碼還要多的信息。並且JavaScript函數值會在內部存儲它們可能會引用的定義在其封閉做用域的變量。那些在其所涵蓋的做用域內跟蹤變量的函數被稱爲閉包。app
tips1:函數能夠引用定義在其外部做用域的變量。tips2:閉包比建立它們的函數有更長的生命週期。函數式編程
函數能夠引用其做用域內的任何變量,包括參數和外部函數變量。利用這點咱們編寫更加通用的formFamily
函數。函數
function formFamily (requiredWho){
function form (who){
return requiredWho + "need " + who ;
}
return form;
}
var f = formFamily("Father ");
f("YingBao and MaoTan"); // "Father need YingBao and MaoTan"
var m = formFamily("Mother ");
m("YingBao and MaoTan"); // "Mother need YingBao and MaoTan"複製代碼
該例子建立了f
和m
倆個徹底不一樣的函數。儘管它們都是由相同的form
函數定義的,可是它們時倆個大相徑庭的對象。優化
閉包是JavaScript最優雅、最有表現力的特性之一。JavaScript甚至還提供了一種更爲方便的構建閉包字面量語法——函數表達式。
function formFamily (requiredWho){
return function(who){
return requiredWho + "need " + who ;
}
}複製代碼
請注意,該函數表達式是匿名的。咱們只需其能產生一個新的函數值,沒打算在局部調用它,所以根本不必給該函數命名。
閉包能夠更新外部變量的值。實際上,閉包存儲的是外部變量的引用,而不是它們的副本。所以,對於任何具備訪問這些外部變量的閉包,均可以進行更新。以下例子。
function box(){
var val = undefined;
return {
set: function(newVal) { val = newVal; },
get: function() { return val; },
type: function() { return typeof val; }
};
}
var b = box();
b.type(); // "undefined"
b.set(13.2);
b.get(); // 13.2.6
b.type(); // "number"複製代碼
box
函數存儲了一個可讀寫的內部值。該例子產生了一個包含三個閉包的對象,這三個閉包是set
、get
和type
屬性。它們都共享訪問val
變量。set
閉包更新val
的值,隨後調用get
和type
查看更新的結果。
function outter(){
var n = 0;
return function (){
return n++;
}
}
var o1 = outter();
o1();//n == 0
o1();//n == 1
o1();//n == 2
var o2 = outter();
o2();//n == 0
o2();//n == 1複製代碼
外部函數outter
執行後返回的是一個function
,賦值給了 o1
,o1
能夠訪問 n
。return
回來的function
能夠訪問裏面的變量,就會致使 n
不能被回收,由於有o1
有用到它。
匿名函數 function(){return n++;} 中
包含對外部函數 outter
的局部變量 n
的引用,所以當外部函數 outter
返回時,n
的值被保留 ( 不會被垃圾回收機制回收 ),持續調用 o1()
,將會改變 n
的值。而 o2
的值並不會隨着 o1()
被調用而改變,第一次調用 o2
會獲得 n==0
的結果,用面向對象的術語來講,就是 o1
和 o2
爲不一樣的實例,互不干涉。
須要注意閉包的一個缺陷:閉包會致使內存泄漏。