js中的閉包
閉包是學(xué)習(xí)js中永遠(yuǎn)也繞不過(guò)去的一個(gè)坎叹洲,那么柠硕,今天我們就去一段簡(jiǎn)單的代碼開始聊一聊閉包
什么是閉包
這個(gè)概念性的東西翻譯有很多種,純官方的翻譯比較晦澀難懂运提,我們把它理解成一個(gè)比較特殊的函數(shù)就行蝗柔。按照網(wǎng)上的說(shuō)法來(lái)說(shuō)就是:
「函數(shù)」和「函數(shù)內(nèi)部能訪問(wèn)到的變量」(也叫環(huán)境)的總和,就是一個(gè)閉包民泵。
function close() {
var n=999;
var getNumber=function () {
return n;
};
return getNumber;
}
在close函數(shù)里面有一個(gè)getNumber方法癣丧,通過(guò)getNumber方法可以訪問(wèn)到函數(shù)的內(nèi)部變量。
閉包的特性
- 能夠讀取函數(shù)內(nèi)部的變量
- 能讓這些變量的值始終保持在內(nèi)存中
第一點(diǎn)很好理解栈妆,我們平時(shí)可能在不知不覺(jué)中就使用了閉包的這個(gè)特性胁编,而第二點(diǎn)雖然用的不多,但是在面試中經(jīng)常遇到鳞尔。首先我們先看一段代碼:
function f1() {
var n=999;
nAdd=function () {
n++;
};
function f2() {
console.log(n);
}
return f2;
}
var result=f1();
result();//999
nAdd();
result();//1000
為什么第二次執(zhí)行后打印的結(jié)果是1000赠涮,而不是999击你,因?yàn)閚被保存下來(lái)了,并沒(méi)有被內(nèi)存回收機(jī)制回收营袜。
為什么每有被回收洛史?因?yàn)閒1是f2的父函數(shù)央勒,而f2被賦給了一個(gè)全局變量寺滚,這導(dǎo)致f2始終在內(nèi)存中斑鼻,而f2的存在依賴于f1,因此f1也始終在內(nèi)存中萤彩,不會(huì)在調(diào)用結(jié)束后粪滤,被垃圾回收機(jī)制(garbage collection)回收。
面試題解讀
這是一個(gè)很常見的閉包面試題:
var result=[];
function foo(){
var i= 0;
for (;i<3;i=i+1){
result[i]=function () {
alert(i);
}
}
}
foo();
result[0](); // 3
result[1](); // 3
result[2](); // 3
運(yùn)行結(jié)果為什么是3乒疏,我簡(jiǎn)單的重現(xiàn)一下代碼運(yùn)行的過(guò)程额衙,
i=0;
result[0]=function(){alert(i)};
i=1;
result[1]=function(){alert(i)};
i=2;
result[2]=function(){alert(i)};
運(yùn)行的時(shí)候在function內(nèi)部放的是一個(gè)變量i,只有被執(zhí)行的時(shí)候才會(huì)給這個(gè)i賦值怕吴。當(dāng)執(zhí)行result[0]的時(shí)候窍侧,里面的變量i因?yàn)樵诋?dāng)前作用域下并沒(méi)有被定義,所以向它的父級(jí)去找转绷,此時(shí)for循環(huán)已經(jīng)執(zhí)行完畢伟件,i的值是3,所以彈出的都是3议经。為什么i保存下來(lái)了斧账,因?yàn)閞esult函數(shù)依賴于foo函數(shù),所以foo一直在內(nèi)存中煞肾,i變量也沒(méi)有被內(nèi)存回收機(jī)制回收咧织。
那么如何實(shí)現(xiàn)彈出的是0,1籍救,2呢习绢?
利用之前提到的閉包就可以了,代碼如下:
var result=[];
function foo(){
var i= 0;
for (;i<3;i=i+1){
(function () {
var index=i;
result[i]=function () {
alert(index);
}
})()
}
}
foo();
result[0](); // 0
result[1](); // 1
result[2](); // 2
利用立即執(zhí)行函數(shù)(IIF)蝙昙,我們可以創(chuàng)建一個(gè)閉包闪萄,在這個(gè)IIF內(nèi)部,我們使用index將i給保存下來(lái)了奇颠。具體執(zhí)行過(guò)程如下:
i=0;
(function () {
var index=0;
result[0]=function () {
alert(index);
}
})()
i=1;
(function () {
var index=1;
result[1]=function () {
alert(index);
}
})()
i=2;
(function () {
var index=2;
result[2]=function () {
alert(index);
}
})()
因?yàn)檫@些語(yǔ)句都放在IIF中败去,所以都有各自的作用域,index并不會(huì)重復(fù)烈拒。就像下方的代碼:
var sayHi=function(){
var words="hi"
}
var sayHello=function(){
var words="hello"
}
兩個(gè)方法中雖然都有words這個(gè)變量圆裕,但是因?yàn)樵诓煌暮瘮?shù)中,都有各自的作用域荆几,所以互不干擾吓妆。