定義函數(shù)的方法:Function類型
匿名函數(shù)——function關(guān)鍵字后面沒(méi)有標(biāo)識(shí)符的函數(shù)。
閉包——有權(quán)訪問(wè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;
}
}
}
一般來(lái)說(shuō),當(dāng)函數(shù)執(zhí)行完畢后,局部活動(dòng)對(duì)象就會(huì)被銷毀,內(nèi)存中僅保存全局作用于(全局執(zhí)行環(huán)境的變量對(duì)象)。但是,閉包的情況有所不同。
var compare = createComparisonFunction("name");
var result = compare({name:"Wonder"},{name:"Greg"});
上面代碼執(zhí)行時(shí),匿名函數(shù)從createComparisonFunction()中被返回后,它的作用域鏈被初始化為包含createComparisonFunction()函數(shù)的活動(dòng)對(duì)象和全局變量對(duì)象。這樣,匿名函數(shù)就可以訪問(wèn)在createComparisonFunction()中定義的所有變量。
更重要的是,createComparisonFunction()函數(shù)在執(zhí)行完畢后,其活動(dòng)對(duì)象也不會(huì)被銷毀,因?yàn)槟涿瘮?shù)的作用域鏈仍然在引用這個(gè)活動(dòng)對(duì)象。即當(dāng)createComparisonFunction()函數(shù)返回后,其執(zhí)行環(huán)境的作用域鏈會(huì)被銷毀,但它的活動(dòng)對(duì)象仍然會(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"});
//接觸對(duì)匿名函數(shù)的引用(以便釋放內(nèi)存
compare = null;
由于閉包會(huì)攜帶包含它的函數(shù)的作用域,因此會(huì)比其他函數(shù)占用更多的內(nèi)存。過(guò)度使用閉包可能會(huì)導(dǎo)致內(nèi)存占用過(guò)多。
閉包與變量
閉包只能取得包含函數(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)對(duì)象,所以它們引用的都是同一個(gè)變量i。當(dāng)createFunctions()函數(shù)返回后,變量i的值是10,此時(shí)每個(gè)函數(shù)都引用這保存變量i的同一個(gè)變量對(duì)象,所以在每個(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對(duì)象
匿名函數(shù)的執(zhí)行環(huán)節(jié)具有全局性,因此this對(duì)象通常指向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)對(duì)象為止,因此永遠(yuǎn)不可能直接訪問(wèn)外部函數(shù)中的這兩個(gè)變量。
解決方案:把外部作用域中的this對(duì)象保存在一個(gè)閉包能夠訪問(wèn)到的變量里,就可以讓閉包訪問(wèn)該對(duì)象了。
var name = "window";
var object = {
name:"object",
getNameFunc: function () {
var that = this;
return function () {
return that.name;
}
}
}
alert(object.getNameFunc()()); //object
內(nèi)存泄漏
IE9之前的版本對(duì)JScript對(duì)象和COM對(duì)象使用不同的垃圾回收例程,因此閉包在IE的這些版本中會(huì)導(dǎo)致一些特殊的問(wèn)題。
如果閉包的作用域鏈中保存著一個(gè)HTML元素,那么就意味著該元素將無(wú)法被銷毀。
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;
}