定義函數(shù)的方法:Function類型
匿名函數(shù)——function關(guān)鍵字后面沒有標(biāo)識(shí)符的函數(shù)萍摊。
閉包——有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)养涮。
//創(chuàng)建閉包的方法:在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)巧骚。
function createComparisonFunction(propertyName){
return function (object1,object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
}
}
一般來說速客,當(dāng)函數(shù)執(zhí)行完畢后迹淌,局部活動(dòng)對象就會(huì)被銷毀对妄,內(nèi)存中僅保存全局作用于(全局執(zhí)行環(huán)境的變量對象)湘今。但是,閉包的情況有所不同剪菱。
var compare = createComparisonFunction("name");
var result = compare({name:"Wonder"},{name:"Greg"});
上面代碼執(zhí)行時(shí),匿名函數(shù)從createComparisonFunction()中被返回后旗们,它的作用域鏈被初始化為包含createComparisonFunction()函數(shù)的活動(dòng)對象和全局變量對象。這樣构灸,匿名函數(shù)就可以訪問在createComparisonFunction()中定義的所有變量上渴。
更重要的是,createComparisonFunction()函數(shù)在執(zhí)行完畢后喜颁,其活動(dòng)對象也不會(huì)被銷毀,因?yàn)槟涿瘮?shù)的作用域鏈仍然在引用這個(gè)活動(dòng)對象洛巢。即當(dāng)createComparisonFunction()函數(shù)返回后,其執(zhí)行環(huán)境的作用域鏈會(huì)被銷毀稿茉,但它的活動(dòng)對象仍然會(huì)留在內(nèi)存中;直到匿名函數(shù)被銷毀后漓库,createComparisonFunction()的活動(dòng)隊(duì)想才會(huì)被銷毀。
//創(chuàng)建函數(shù)
var compare = createComparisonFunction("name");
//調(diào)用函數(shù)
var result = compare({name:"Wonder"},{name:"Greg"});
//接觸對匿名函數(shù)的引用(以便釋放內(nèi)存
compare = null;
由于閉包會(huì)攜帶包含它的函數(shù)的作用域痢士,因此會(huì)比其他函數(shù)占用更多的內(nèi)存。過度使用閉包可能會(huì)導(dǎo)致內(nèi)存占用過多怠蹂。
閉包與變量
閉包只能取得包含函數(shù)中任何變量的最后一個(gè)值少态。
function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function () {
return i;
};
}
return result;
}
var re = createFunctions();
alert(re[0]()); //10
這個(gè)函數(shù)會(huì)返回一個(gè)函數(shù)數(shù)組。表面上看彼妻,似乎每個(gè)函數(shù)都應(yīng)該返回自己的索引值,但實(shí)際上侨歉,每個(gè)函數(shù)都返回10。因?yàn)槊總€(gè)函數(shù)的作用域鏈中都保存著createFunctions()函數(shù)的活動(dòng)對象炮温,所以它們引用的都是同一個(gè)變量i。當(dāng)createFunctions()函數(shù)返回后茅特,變量i的值是10棋枕,此時(shí)每個(gè)函數(shù)都引用這保存變量i的同一個(gè)變量對象,所以在每個(gè)函數(shù)內(nèi)部i的值都是10重斑。
解決辦法:創(chuàng)建另一個(gè)匿名函數(shù)強(qiáng)制讓閉包的行為符合預(yù)期。
function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function (num) {
return function () {
return num;
};
}(i);
}
return result;
}
var re = createFunctions();
alert(re[0]());
關(guān)于this對象
匿名函數(shù)的執(zhí)行環(huán)節(jié)具有全局性窥浪,因此this對象通常指向window。
var name = "window";
var object = {
name:"object",
getNameFunc: function () {
return function () {
return this.name;
}
}
}
alert(object.getNameFunc()()); //(非嚴(yán)格模式下)window
每個(gè)函數(shù)在被調(diào)用時(shí)都會(huì)自動(dòng)取得兩個(gè)特殊變量:this和arguments假颇。內(nèi)部函數(shù)在搜索這兩個(gè)變量時(shí)骨稿,只會(huì)搜索到其活動(dòng)對象為止笨鸡,因此永遠(yuǎn)不可能直接訪問外部函數(shù)中的這兩個(gè)變量。
解決方案:把外部作用域中的this對象保存在一個(gè)閉包能夠訪問到的變量里哥桥,就可以讓閉包訪問該對象了。
var name = "window";
var object = {
name:"object",
getNameFunc: function () {
var that = this;
return function () {
return that.name;
}
}
}
alert(object.getNameFunc()()); //object
內(nèi)存泄漏
IE9之前的版本對JScript對象和COM對象使用不同的垃圾回收例程激涤,因此閉包在IE的這些版本中會(huì)導(dǎo)致一些特殊的問題。
如果閉包的作用域鏈中保存著一個(gè)HTML元素倦踢,那么就意味著該元素將無法被銷毀。
function assignHandler(){
var element = document.getElementById("someElement");
element.onclick = function(){
alert(element.id);
}
}
解決方案:
function assignHandler(){
var element = document.getElementById("someElement");
var id = element.id;
element.onclick = function(){
alert(id);
}
element = null;
}