來(lái)看一些關(guān)于閉包的定義:
1.閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中變量的函數(shù) --《JS高級(jí)程序設(shè)計(jì)第三版》 p178
2.函數(shù)對(duì)象可以通過(guò)作用域鏈相關(guān)聯(lián)起來(lái)蚣抗,函數(shù)體內(nèi)部的變量都可以保存在函數(shù)作用域內(nèi)侈百,這種特性稱(chēng)為 ‘閉包’ 悯周。 --《JS權(quán)威指南》 p183
3.內(nèi)部函數(shù)可以訪問(wèn)定義它們的外部函數(shù)的參數(shù)和變量(除了this和arguments)前塔。 --《JS語(yǔ)言精粹》p36
來(lái)個(gè)定義總結(jié)
1.可以訪問(wèn)外部函數(shù)作用域中變量的函數(shù)
2.被內(nèi)部函數(shù)訪問(wèn)的外部函數(shù)的變量可以保存在外部函數(shù)作用域內(nèi)而不被回收---這是核心,后面我們遇到閉包都要想到妇萄,我們要重點(diǎn)關(guān)注被閉包引用的這個(gè)變量锭魔。
創(chuàng)建一個(gè)閉包:
var sayName =function(){
????var name ='jozo';
????return function(){?
?????????alert(name);
?????}
};
var say = sayName();
?say();
var say = sayName()?:返回了一個(gè)匿名的內(nèi)部函數(shù)保存在變量say中例证,并且引用了外部函數(shù)的變量name,由于垃圾回收機(jī)制迷捧,sayName函數(shù)執(zhí)行完畢后织咧,變量name并沒(méi)有被銷(xiāo)毀。
say()?:執(zhí)行返回的內(nèi)部函數(shù)漠秋,依然能訪問(wèn)變量name,輸出 'jozo' .
2. 閉包中的作用域鏈
變量在作用域中的查找方式應(yīng)該都很熟悉了笙蒙,其實(shí)這就是順著作用域鏈往上查找的。
當(dāng)函數(shù)被調(diào)用時(shí):
1.先創(chuàng)建一個(gè)執(zhí)行環(huán)境(execution context),及相應(yīng)的作用域鏈庆锦;
2.將arguments和其他命名參數(shù)的值添加到函數(shù)的活動(dòng)對(duì)象(activation object)
作用域鏈:當(dāng)前函數(shù)的活動(dòng)對(duì)象優(yōu)先級(jí)最高捅位,外部函數(shù)的活動(dòng)對(duì)象次之,外部函數(shù)的外部函數(shù)的活動(dòng)對(duì)象依次遞減搂抒,直至作用域鏈的末端--全局作用域艇搀。優(yōu)先級(jí)就是變量查找的先后順序;
3. 閉包的實(shí)例
實(shí)例1:實(shí)現(xiàn)累加
// 方式1
var a =0;
var add=function(){
? ? a++;
? ? console.log(a)
}
add();
add();
//方式2 :閉包
var add= (function(){
? ? var? a = 0;
? ? return function(){
? ? ? ? a++;
? ? ? ? console.log(a);
? ? }})();
console.log(a);//undefined
add();
add();
實(shí)例2 :給每個(gè)li添加點(diǎn)擊事件
var oli =document.getElementsByTagName('li');
var i;
for(i =0;i <5;i++){?
? ???oli[i].onclick =function(){?
?? ??????alert(i);?
?? ??}?
}
console.log(i);// 5
//執(zhí)行匿名函數(shù)
(function(){?
?alert(i);? ?//5
}());?
上面是一個(gè)經(jīng)典的例子求晶,我們都知道執(zhí)行結(jié)果是都彈出5焰雕,也知道可以用閉包解決這個(gè)問(wèn)題,但是我剛開(kāi)始始終不能明白為什么每次彈出都是5芳杏,為什么閉包可以解決這問(wèn)題矩屁。后來(lái)捋一捋還是把它弄清晰了:
a.?先來(lái)分析沒(méi)用閉包前的情況:for循環(huán)中,我們給每個(gè)li點(diǎn)擊事件綁定了一個(gè)匿名函數(shù)辟宗,匿名函數(shù)中返回了變量i的值,當(dāng)循環(huán)結(jié)束后档插,變量i的值變?yōu)?慢蜓,此時(shí)我們?cè)偃c(diǎn)擊每個(gè)li,也就是執(zhí)行相應(yīng)的匿名函數(shù)(看上面的代碼)郭膛,這是變量i已經(jīng)是5了晨抡,所以每個(gè)點(diǎn)擊彈出5. 因?yàn)檫@里返回的每個(gè)匿名函數(shù)都是引用了同一個(gè)變量i,如果我們新建一個(gè)變量保存循環(huán)執(zhí)行時(shí)當(dāng)前的i的值则剃,然后再讓匿名函數(shù)應(yīng)用這個(gè)變量耘柱,最后再返回這個(gè)匿名函數(shù),這樣就可以達(dá)到我們的目的了棍现,這就是運(yùn)用閉包來(lái)實(shí)現(xiàn)的调煎!
4. 閉包的運(yùn)用
01.匿名自執(zhí)行函數(shù)
02.實(shí)現(xiàn)封裝/模塊化代碼
var person=function(){
? ??//變量作用域?yàn)楹瘮?shù)內(nèi)部,外部無(wú)法訪問(wèn)?
?? ??var name ="default";
? ??return{
? ??????????getName:function(){
? ??????????????????return name;?
? ??????????},
? ??????setName:function(newName){?
?? ??????????name = newName;?
? ???????}
? ???}?
?}();
console.log(person.name);//直接訪問(wèn)己肮,結(jié)果為undefined?
console.log(person.getName());//default?
person.setName("jozo");
console.log(person.getName());//jozo
03. 實(shí)現(xiàn)面向?qū)ο笾械膶?duì)象