什么是閉包
「函數(shù)」和「函數(shù)內(nèi)部能訪問(wèn)到的變量」(也叫環(huán)境)的總和盏缤,就是一個(gè)閉包修肠。
有些人說(shuō)閉包就是函數(shù)套函數(shù)古程,然后 return 一個(gè)函數(shù)。就像這樣:
function foo(){
var local = 1;
function bar(){
local ++;
return local;
}
return bar;
}
var func = foo()
console.log(func());
這里面確實(shí)有閉包屯伞,local 變量和 bar 函數(shù)就組成了一個(gè)閉包(Closure)
為什么要函數(shù)套函數(shù)呢腿箩?
是因?yàn)樾枰植孔兞浚圆虐?local 放在一個(gè)函數(shù)里劣摇,如果不把 local 放在一個(gè)函數(shù)里珠移,local 就是一個(gè)全局變量了,達(dá)不到使用閉包的目的——隱藏變量末融。
有些人看到「閉包」這個(gè)名字钧惧,就一定覺(jué)得要用什么包起來(lái)才行。其實(shí)這是翻譯問(wèn)題勾习,閉包的原文是 Closure浓瞪,跟「包」沒(méi)有任何關(guān)系。
所以函數(shù)套函數(shù)只是為了造出一個(gè)局部變量巧婶,跟閉包無(wú)關(guān)乾颁。
為什么要 return bar 呢?
因?yàn)槿绻?return艺栈,你就無(wú)法使用這個(gè)閉包英岭。把 return bar 改成 window.bar = bar 也是一樣的,只要讓外面可以訪問(wèn)到這個(gè) bar 函數(shù)就行了湿右。
所以 return bar 只是為了 bar 能被使用巴席,也跟閉包無(wú)關(guān)。
閉包會(huì)造成內(nèi)存泄露诅需?
錯(cuò)漾唉。
內(nèi)存泄露是指你用不到(訪問(wèn)不到)的變量荧库,依然占居著內(nèi)存空間,不能被再次利用起來(lái)赵刑。
閉包里面的變量明明就是我們需要的變量(lives)分衫,憑什么說(shuō)是內(nèi)存泄露?
這個(gè)謠言是如何來(lái)的般此?
因?yàn)?IE蚪战。IE 有 bug,IE 在我們使用完閉包之后铐懊,依然回收不了閉包里面引用的變量邀桑。
這是 IE 的問(wèn)題,不是閉包的問(wèn)題
現(xiàn)在我們來(lái)看一個(gè)關(guān)于閉包的題:
檢測(cè)一下我們對(duì)閉包的掌握的情況科乎。
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?
//問(wèn):三行a,b,c的輸出分別是什么壁畸?
//a: undefined,0,0,0
//b: undefined,0,1,2
//c: undefined,0,1,1
js函數(shù)的分類
具名函數(shù)(命名函數(shù))和匿名函數(shù)。
創(chuàng)建函數(shù)的幾種方式
1.聲明函數(shù)
functio fun(){ }
2.創(chuàng)建匿名函數(shù)表達(dá)式
var fun = function(){ }
//實(shí)際執(zhí)行順序?yàn)?// var fun;
// function fun(){ }
3.創(chuàng)建具名函數(shù)表達(dá)式
var fun = function funName(){ }
4.function構(gòu)造函數(shù)
可以給 Function 構(gòu)造函數(shù)傳一個(gè)函數(shù)字符串茅茂,返回包含這個(gè)字符串命令的函數(shù)捏萍,此種方法創(chuàng)建的是匿名函數(shù)。
5.自執(zhí)行函數(shù)
(function(){alert(1)})();
(function fn1(){alert(1);})();
分析上述三個(gè)函數(shù)的關(guān)系
function fun(n,o) { //標(biāo)準(zhǔn)具名函數(shù)空闲,返回的的是一個(gè)對(duì)象字面量表達(dá)式
console.log(o)
return {
fun:function(m){ //匿名函數(shù)表達(dá)式
//...
}
};
}
函數(shù)作用域鏈的問(wèn)題
1.對(duì)象內(nèi)部的函數(shù)表達(dá)式
var obj = {
fn:function(){
console.log(fn);
}
}
obj.fn() //error(fn is not defined)
2.非對(duì)象內(nèi)部函數(shù)表達(dá)式
var fn = function(){
console.log(fn)
}
fn() //function (){console.log(fn);};
結(jié)論是:使用var或是非對(duì)象內(nèi)部的函數(shù)表達(dá)式內(nèi)令杈,可以訪問(wèn)到存放當(dāng)前函數(shù)的變量;在對(duì)象內(nèi)部的不能訪問(wèn)到碴倾。
原因也非常簡(jiǎn)單逗噩,因?yàn)楹瘮?shù)作用域鏈的問(wèn)題,采用var的是在外部創(chuàng)建了一個(gè)fn變量跌榔,函數(shù)內(nèi)部當(dāng)然可以在內(nèi)部尋找不到fn后向上冊(cè)作用域查找fn异雁,而在創(chuàng)建對(duì)象內(nèi)部時(shí),因?yàn)闆](méi)有在函數(shù)作用域內(nèi)創(chuàng)建fn矫户,所以無(wú)法訪問(wèn)片迅。
所以綜上所述,可以得知皆辽,最內(nèi)層的return出去的fun函數(shù)不是第二層fun函數(shù)柑蛇,是最外層的fun函數(shù)。
到底在調(diào)用那個(gè)函數(shù)
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?
//問(wèn):三行a,b,c的輸出分別是什么驱闷?
第一行:
var a = fun(0); a.fun(1); a.fun(2); a.fun(3)
可以得知耻台,第一個(gè)fun(0)是在調(diào)用第一層fun函數(shù)。第二個(gè)fun(1)是在調(diào)用前一個(gè)fun的返回值的fun函數(shù)空另,所以:
第后面幾個(gè)fun(1),fun(2),fun(3),函數(shù)都是在調(diào)用第二層fun函數(shù)盆耽。
遂:
在第一次調(diào)用fun(0)時(shí),o為undefined;
第二次調(diào)用fun(1)時(shí)m為1摄杂,此時(shí)fun閉包了外層函數(shù)的n坝咐,也就是第一次調(diào)用的n=0,即m=1析恢,n=0墨坚,并在內(nèi)部調(diào)用第一層fun函數(shù)fun(1,0);所以o為0;
第三次調(diào)用fun(2)時(shí)m為2映挂,但依然是調(diào)用a.fun泽篮,所以還是閉包了第一次調(diào)用時(shí)的n,所以內(nèi)部調(diào)用第一層的fun(2,0);所以o為0
第四次同理柑船;
即:最終答案為undefined,0,0,0
第二行
var b = fun(0).fun(1).fun(2).fun(3)
先從fun(0)開(kāi)始看帽撑,肯定是調(diào)用的第一層fun函數(shù);而他的返回值是一個(gè)對(duì)象鞍时,所以第二個(gè)fun(1)調(diào)用的是第二層fun函數(shù)亏拉,后面幾個(gè)也是調(diào)用的第二層fun函數(shù)。
遂:
在第一次調(diào)用第一層fun(0)時(shí),o為undefined;
第二次調(diào)用 .fun(1)時(shí)m為1忿墅,此時(shí)fun閉包了外層函數(shù)的n陡鹃,也就是第一次調(diào)用的n=0,即m=1吮旅,n=0溪烤,并在內(nèi)部調(diào)用第一層fun函數(shù)fun(1,0);所以o為0;
第三次調(diào)用 .fun(2)時(shí)m為2庇勃,此時(shí)當(dāng)前的fun函數(shù)不是第一次執(zhí)行的返回對(duì)象檬嘀,而是第二次執(zhí)行的返回對(duì)象。而在第二次執(zhí)行第一層fun函數(shù)時(shí)時(shí)(1,0)所以n=1,o=0,返回時(shí)閉包了第二次的n责嚷,遂在第三次調(diào)用第三層fun函數(shù)時(shí)m=2,n=1鸳兽,即調(diào)用第一層fun函數(shù)fun(2,1),所以o為1罕拂;
第四次調(diào)用 .fun(3)時(shí)m為3揍异,閉包了第三次調(diào)用的n,同理爆班,最終調(diào)用第一層fun函數(shù)為fun(3,2)衷掷;所以o為2;
即最終答案:undefined,0,1,2
第三行
var c = fun(0).fun(1); c.fun(2); c.fun(3)
根據(jù)前面兩個(gè)例子柿菩,可以得知:
fun(0)為執(zhí)行第一層fun函數(shù)戚嗅,.fun(1)執(zhí)行的是fun(0)返回的第二層fun函數(shù),這里語(yǔ)句結(jié)束,遂c存放的是fun(1)的返回值懦胞,而不是fun(0)的返回值替久,所以c中閉包的也是fun(1)第二次執(zhí)行的n的值。c.fun(2)執(zhí)行的是fun(1)返回的第二層fun函數(shù)躏尉,c.fun(3)執(zhí)行的也是fun(1)返回的第二層fun函數(shù)侣肄。
遂:
在第一次調(diào)用第一層fun(0)時(shí),o為undefined醇份;
第二次調(diào)用 .fun(1)時(shí)m為1稼锅,此時(shí)fun閉包了外層函數(shù)的n,也就是第一次調(diào)用的n=0僚纷,即m=1矩距,n=0,并在內(nèi)部調(diào)用第一層fun函數(shù)fun(1,0);所以o為0怖竭;
第三次調(diào)用 .fun(2)時(shí)m為2锥债,此時(shí)fun閉包的是第二次調(diào)用的n=1,即m=2痊臭,n=1哮肚,并在內(nèi)部調(diào)用第一層fun函數(shù)fun(2,1);所以o為1;
第四次.fun(3)時(shí)同理广匙,但依然是調(diào)用的第二次的返回值允趟,遂最終調(diào)用第一層fun函數(shù)fun(3,1),所以o還為1
即最終答案:undefined,0,1,1