
一、閉包有什么用
1、能夠在函數(shù)外部引用函數(shù)內(nèi)部的變量(變量作用域);
2、讓變量的值始終保持在內(nèi)存中(垃圾回收機制),避免全局變量的污染;
3、私有成員的存在。
二、為什么要引入閉包
? ? ? 在第一點說明了使用閉包的好處,備注里說明了使用閉包的原因。正是為了解決JavaScript變量作用域和垃圾回收機制所帶來的問題耳引出的閉包。
1、變量作用域:
在JavaScript中,沒有塊級左右域,只有全局作用域和局部作用域,如下:
例1:? 函數(shù)內(nèi)部可以直接讀取全局變量
var n=999;
function f1(){
? ? ? ?alert(n);
}
f1(); ?// 999
例2:? 函數(shù)外部無法讀取函數(shù)內(nèi)的局部變量
function f1(){
? ? ? var n=999;
}
alert(n);// error
例3:函數(shù)內(nèi)聲明變量,沒有使用var,相當(dāng)于定義了全局變量
function f1(){
? ? ? ?n=999;
}
f1();
alert(n);// 999
2、JS垃圾回收機制
? ? ? JavaScript中,會按照固定的時間回收不再使用的變量,以釋放其所占用的內(nèi)存。通過閉包能夠避免變量被回收。
三、如何使用閉包
1、在函數(shù)外部獲取內(nèi)部變量
例4:function f1(){
? ? ? ? ? ? n=999;
? ? ? ? ? ?function f2(){
? ? ? ? ? ?alert(n);// 999
? ? ?}
}
既然f2能夠訪問f1 中的所有變量,所以將f2作為返回值,便可 在f1外訪問f1內(nèi)的變量了,如例5:
例5:function f1(){
? ? ? ? ? ?n=999;
? ? ? ? ? ?function f2(){
? ? ? ? ? ? ? ? ? ?alert(n);
? ? ? ? ? ?}
? ? ? ? ?return f2;
}
var result=f1();
result();// 999
2、將變量保存在內(nèi)存中,例6.
例6:
function f1(){
? ? ? ? ?var n=999;
? ? ? ? ?nAdd=function(){
? ? ? ? ? ? ? ? n+=1
? ? ? ? ? }
? ? ? ? ? function f2(){
? ? ? ? ? ? ? ? ? ?alert(n);
? ? ? ? ? ?}
? ? ? ? ? return f2;
}
var result=f1();
result();// 999
nAdd();
result();// 1000
? ? ? ?結(jié)果顯示,它一共運行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數(shù)f1中的局部變量n一直保存在內(nèi)存中,并沒有在f1調(diào)用后被自動清除。
? ? ? ? 這是因為f1 和 f2 ?相互引用,f1是f2的父函數(shù),而f2被賦給了一個全局變量,這導(dǎo)致f2始終在內(nèi)存中,而f2的存在依賴于f1,因此f1也始終在內(nèi)存中,不會在調(diào)用結(jié)束后,被垃圾回收機制(garbage collection)回收。
四、閉包例子
例7:
vari =5;
function a() {
? ? ? ?var i =0;
? ? ? ?function b() {
? ? ? ? ? ? ? alert(i++);
? ? ? ? }
? ? ? ?return b;
}
a()(); ? //0
例8:
var name ='The Window';
var object1 = {
? ? ? ?name:'My Object',
? ? ? ?getNameFunc:function() {
? ? ? ? ? ? ? ? return function() {
? ? ? ? ? ? ? ? ? ? ? ?return this.name;
? ? ? ? ? ? ? ?};
? ? ? ? }
};
alert(object1.getNameFunc()()); ? //The Window
? ? ? 解釋一下例8.? object1.getNameFunc() 的結(jié)果是第一個return 的內(nèi)容,object1.getNameFunc()() 實際上就相當(dāng)于在全局環(huán)境下執(zhí)行了第一個return 的內(nèi)容,所以this ?指向的是window, 所以this.name = window.name = 'The Window'. ?
例9:
a、 ??
var data = [];
for (var i = 0; i < 3; i++) {
? ? ? ? ? ? console.log('i:'+i);
? ? ? ? ? ? data[i] = function () {
? ? ? ? ? ? ? ? ? ? ?console.log(i)
? ? ? ? ? ? ? }
}
data[0](); ?//3
data[1](); ?//3
data[2](); ?//3
b、
var data = [];
for(vari =0;i <3;i ++){
? ? ? ? console.log('i:'+i);
? ? ? ? data[i] = (function(i) {
? ? ? ? ? ? ? ? ? return function() {
? ? ? ? ? ? ? ? ? ? ? ? ? console.log(i);
? ? ? ? ? ? ? ? }
? ? ? ? })(i)
}
data[0](); ? ?//0
data[1](); ? ?//1
data[2](); ? //2
五、使用閉包的注意點
1、由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。
注:順便提一下內(nèi)存泄漏和內(nèi)存溢出的區(qū)別。
內(nèi)存泄露:memory leak,指程序申請了內(nèi)存但不歸還,導(dǎo)致這些內(nèi)存無法再配分配自己也無法使用。
內(nèi)存溢出:out of memory,指內(nèi)存不夠用了。假如你定義了一個Interger,卻給了一個 Long 的值,便會內(nèi)存溢出。
在用戶使用中,少量的內(nèi)存泄露感覺不出,但內(nèi)存泄露堆積后果很嚴(yán)重,將導(dǎo)致內(nèi)存溢出。
2、閉包會在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當(dāng)作對象(object)使用,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value),這時一定要小心,不要隨便。
舉個例子:
function aaa() {?
? ? ? ?var c =1;
? ? ? ? return function() {
? ? ? ? ? ? ? alert(c++);
? ? ? ? ?};
}
var ? fun =aaa();
fun();//1
fun();//2
fun =null;//回收
var ?fun =aaa();
fun();//1
function aaa() {
? ? ? ? ?var c =1;
? ? ? ? ?return ?function() {
? ? ? ? ? ? ? ? ? ?alert(c++);
? ? ? ? ? };
}
aaa()();//1
aaa()();//1
var? fun =aaa();
fun();//1
此篇文章參考自:(http://www.jb51.net/article/24101.htm)