js中的閉包
閉包是學(xué)習(xí)js中永遠(yuǎn)也繞不過去的一個坎,那么,今天我們就去一段簡單的代碼開始聊一聊閉包
什么是閉包
這個概念性的東西翻譯有很多種,純官方的翻譯比較晦澀難懂,我們把它理解成一個比較特殊的函數(shù)就行。按照網(wǎng)上的說法來說就是:
「函數(shù)」和「函數(shù)內(nèi)部能訪問到的變量」(也叫環(huán)境)的總和,就是一個閉包。
function close() {
var n=999;
var getNumber=function () {
return n;
};
return getNumber;
}
在close函數(shù)里面有一個getNumber方法,通過getNumber方法可以訪問到函數(shù)的內(nèi)部變量。
閉包的特性
- 能夠讀取函數(shù)內(nèi)部的變量
- 能讓這些變量的值始終保持在內(nèi)存中
第一點(diǎn)很好理解,我們平時可能在不知不覺中就使用了閉包的這個特性,而第二點(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)閚被保存下來了,并沒有被內(nèi)存回收機(jī)制回收。
為什么每有被回收?因?yàn)閒1是f2的父函數(shù),而f2被賦給了一個全局變量,這導(dǎo)致f2始終在內(nèi)存中,而f2的存在依賴于f1,因此f1也始終在內(nèi)存中,不會在調(diào)用結(jié)束后,被垃圾回收機(jī)制(garbage collection)回收。
面試題解讀
這是一個很常見的閉包面試題:
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,我簡單的重現(xiàn)一下代碼運(yùn)行的過程,
i=0;
result[0]=function(){alert(i)};
i=1;
result[1]=function(){alert(i)};
i=2;
result[2]=function(){alert(i)};
運(yùn)行的時候在function內(nèi)部放的是一個變量i,只有被執(zhí)行的時候才會給這個i賦值。當(dāng)執(zhí)行result[0]的時候,里面的變量i因?yàn)樵诋?dāng)前作用域下并沒有被定義,所以向它的父級去找,此時for循環(huán)已經(jīng)執(zhí)行完畢,i的值是3,所以彈出的都是3。為什么i保存下來了,因?yàn)閞esult函數(shù)依賴于foo函數(shù),所以foo一直在內(nèi)存中,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)建一個閉包,在這個IIF內(nèi)部,我們使用index將i給保存下來了。具體執(zhí)行過程如下:
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)檫@些語句都放在IIF中,所以都有各自的作用域,index并不會重復(fù)。就像下方的代碼:
var sayHi=function(){
var words="hi"
}
var sayHello=function(){
var words="hello"
}
兩個方法中雖然都有words這個變量,但是因?yàn)樵诓煌暮瘮?shù)中,都有各自的作用域,所以互不干擾。