今天禽捆,看到j(luò)avaScript閉包這一塊的地方,不得不說飘哨,第一次接觸這樣的函數(shù)行為胚想,是很難理解的,也是研究了半天芽隆,才總算理解了閉包這個(gè)牛逼的“東西”浊服;
首先,什么是閉包胚吁,簡單來說牙躺,就是內(nèi)部函數(shù)(inner function)可以訪問外部函數(shù)(outer function)的變量以及參數(shù)(注意inner function不能直接調(diào)用outer function的arguments對象,但可以直接使用參數(shù))囤采;由于述呐,有作用域鏈(scope chain)的存在,導(dǎo)致閉包在outer function即使在被調(diào)用之后蕉毯,inner function依然可以訪問outer function的變量乓搬,這意味著,本來應(yīng)該被銷毀的局部變量代虾,卻一直保留在了內(nèi)存中进肯。
下面看一個(gè)簡單的閉包例子:
function showName(firstName,lastName) {
var nameInfo="My name is ";
function makeFullName() {
return nameInfo+firstName+" "+lastName;
}
return makeFullName;
}
showName("athan","Zhang")();
//會顯示My name is athan Zhang
上面這個(gè)例子,簡單的解釋了一下內(nèi)部函數(shù)可以直接調(diào)用外部函數(shù)的變量棉磨,以及直接使用外部函數(shù)的參數(shù)江掩。
接下來,看一個(gè)比較復(fù)雜的情況:
function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function () {
return uniqueID + i;
}
}
return theCelebrities;
}
var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0},{name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id());
var cruiseID = createIdForActionCelebs [1];
console.log(cruiseID.id());
這個(gè)例子中乘瓤,console打印出的結(jié)果都是103环形。因?yàn)関ar createIdForActionCelebs = celebrityIDCreator (actionCelebs) 這里在調(diào)用外部函數(shù)celebrityIDCreator時(shí),完成了外部函數(shù)里面定義的語句塊的內(nèi)容衙傀,這也意味著抬吟,for循環(huán)也執(zhí)行完畢,數(shù)組theCelebrities的id屬性被賦予了函數(shù)表達(dá)式统抬,但這個(gè)內(nèi)部函數(shù)此時(shí)并沒有被調(diào)用火本。在后面stalloneID.id()顯示調(diào)用內(nèi)部函數(shù)時(shí)危队,返回了uniqueID+i的值,而外部函數(shù)早已調(diào)用完畢钙畔,此時(shí)的i的值茫陆,已經(jīng)是3,所以擎析,無論調(diào)用theCelebrities數(shù)組里面哪一個(gè)對象簿盅,返回的值都是一樣的,這也是閉包存在的一個(gè)問題揍魂,那怎么解決這個(gè)問題呢挪鹏,得看下面的例子。
function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function (j) {
return function () { return uniqueID + j; } () ;
} (i);
}
return theCelebrities;
}
var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id); // 100
var cruiseID = createIdForActionCelebs [1];
console.log(cruiseID.id);
這個(gè)例子里愉烙,使用了一種Immediately Invoked Function Expression(立刻調(diào)用函數(shù)表達(dá)式)的語法。上面的函數(shù)表達(dá)式function(j){/code/}(i)解取,直接在賦值時(shí)就調(diào)用了步责,而且將i的值賦給了j,同樣閉包里面也用了同樣的語法禀苦,這樣就可以在循環(huán)時(shí)蔓肯,就直接調(diào)用內(nèi)部函數(shù),得到想要的值振乏,這個(gè)例子里面打印的就是100和101蔗包。
有一點(diǎn)需要注意的是,函數(shù)表達(dá)式function(j){/code/}(i)和函數(shù)聲明是不一樣的慧邮,如果在以為函數(shù)聲明時(shí)调限,在函數(shù)后面加上()這個(gè)括號,就是直接調(diào)用函數(shù)误澳,是會出錯(cuò)的耻矮,因?yàn)榫幾g器認(rèn)為這個(gè)是函數(shù)聲明,而不是函數(shù)表達(dá)式忆谓。但是如果用一個(gè)括號裆装,將整個(gè)函數(shù)聲明包起來,編譯器會認(rèn)為是一個(gè)表達(dá)式倡缠,就不會報(bào)錯(cuò)哨免,如下面所示: