閉包(closure)是掌握J(rèn)avascript從人門到深入一個(gè)非常重要的門檻,它是Javascript語言的一個(gè)難點(diǎn),也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)。下面寫下我的學(xué)習(xí)筆記~
閉包-無處不在
在前端編程中,使用閉包是非常常見的,我們經(jīng)常有意無意,直接或間接用到了閉包。閉包可以使傳遞數(shù)據(jù)更加靈活(比如處理一些點(diǎn)擊事件)
!function() {
var localData = "localData here";
document.addEventListener('click', //處理點(diǎn)擊事件時(shí)用到了外部局部變量,比如這里的localData
function(){
console.log(localData);
});
}();
又比如下面這個(gè)例子:(是不是很親切~~)
!function() {
var localData = "localData here";
var url = "http://www.baidu.com/";
$.ajax({
url : url,
success : function() {
// do sth...
console.log(localData);
}
});
}();
再來看一個(gè)例子~~這種情況就是我們通常所說的閉包
function outer() {
var localVal = 30;
return function(){
return localVal;
}
}
var func = outer();
func(); // 30
這個(gè)例子中調(diào)用outer()返回匿名函數(shù)function(),這個(gè)匿名函數(shù)中可以訪問outer()的局部變量localVal,在outer()調(diào)用結(jié)束后,再次調(diào)用func()的時(shí)候,仍然能訪問到outer()的局部變量localVal
閉包的概念
閉包,不同于一般的函數(shù),它允許一個(gè)函數(shù)在立即詞法作用域外調(diào)用時(shí),仍可訪問非本地變量。 --維基百科
閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。 --阮一峰
由于在Javascript語言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,因此可以把閉包簡(jiǎn)單理解成"定義在一個(gè)函數(shù)內(nèi)部的函數(shù)"。
所以,在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁
閉包的用途
這部分轉(zhuǎn)自這篇博文
閉包可以用在許多地方。它的最大用處有兩個(gè),一個(gè)是前面提到的可以讀取函數(shù)內(nèi)部的變量,另一個(gè)就是讓這些變量的值始終保持在內(nèi)存中。
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在這段代碼中,result實(shí)際上就是閉包f2函數(shù)。它一共運(yùn)行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數(shù)f1中的局部變量n一直保存在內(nèi)存中,并沒有在f1調(diào)用后被自動(dòng)清除。
為什么會(huì)這樣呢?原因就在于f1是f2的父函數(shù),而f2被賦給了一個(gè)全局變量,這導(dǎo)致f2始終在內(nèi)存中,而f2的存在依賴于f1,因此f1也始終在內(nèi)存中,不會(huì)在調(diào)用結(jié)束后,被垃圾回收機(jī)制(garbage collection)回收。
這段代碼中另一個(gè)值得注意的地方,就是"nAdd=function(){n+=1}"這一行,首先在nAdd前面沒有使用var關(guān)鍵字,因此nAdd是一個(gè)全局變量,而不是局部變量。其次,nAdd的值是一個(gè)匿名函數(shù)(anonymous function),而這個(gè)匿名函數(shù)本身也是一個(gè)閉包,所以nAdd相當(dāng)于是一個(gè)setter,可以在函數(shù)外部對(duì)函數(shù)內(nèi)部的局部變量進(jìn)行操作。
閉包-封裝
(function() {
var _userId = 23492;
var _typeId = 'item';
var export = {};
function converter(userId) {
return +userId;
}
export.getUserId = function() {
return converter(_userId);
}
export.getTypeId = function() {
return _typeId;
}
window.export = export; //通過此方式輸出
}());
export.getUserId(); // 23492
export.getTypeId(); // item
export._userId; // undefined
export._typeId; // undefined
export.converter; // undefined
利用閉包的特性能讓我們封裝一些復(fù)雜的函數(shù)邏輯,在這個(gè)例子中調(diào)用export上的方法(getUserId,getTypeId)間接訪問函數(shù)里私有變量,但是直接調(diào)用export._userId是沒法拿到_userId的。這也是Node里面常用到特性吧~
常見錯(cuò)誤之循環(huán)閉包
下面這個(gè)案例,我們添加3個(gè)div,值分別為aaa,bbb,ccc,我們想實(shí)現(xiàn)的是點(diǎn)擊aaa輸出1,點(diǎn)擊bbb輸出2,點(diǎn)擊ccc輸出3
document.body.innerHTML = "<div id=div1>aaa</div>" + "<div id=div2>bbb</div><div id=div3>ccc</div>";
for (var i = 1; i < 4; i++) {
document.getElementById('div' + i).
addEventListener('click', function() {
alert(i); // all are 4!
});
}
結(jié)果點(diǎn)擊aaa,bbb還是ccc都是alert(4)~~
產(chǎn)生這樣的問題在于這個(gè)i的值在初始化完成的時(shí)候就已經(jīng)是4了
要達(dá)到我們想要的點(diǎn)擊aaa輸出1,點(diǎn)擊bbb輸出2,點(diǎn)擊ccc輸出3,要用到閉包的技巧,在每次循環(huán)的時(shí)候,用立即執(zhí)行的匿名函數(shù)把它包裝起來,這樣子做的話,每次alert(i)的值就取自閉包環(huán)境中的i,這個(gè)i來自每次循環(huán)的賦值i就能輸出1,2,3了
document.body.innerHTML = "<div id=div1>aaa</div>" + "<div id=div2>bbb</div>" + "<div id=div3>ccc</div>";
for (var i = 1; i < 4; i++) {
!function(i){ //②再用這個(gè)參數(shù)i,到getElementById()中引用
document.getElementById('div' + i).
addEventListener('click', function() {
alert(i); // 1,2,3
});
}(i); //①把遍歷的1,2,3的值傳到匿名函數(shù)里面
}
思考題
如果你能理解下面兩段代碼的運(yùn)行結(jié)果,應(yīng)該就算理解閉包的運(yùn)行機(jī)制了。(來自阮老師)這題目總結(jié)得真秒~~
代碼片段一。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());
代碼片段二。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());
哈爾濱品用軟件有限公司致力于為哈爾濱的中小企業(yè)制作大氣、美觀的優(yōu)秀網(wǎng)站,并且能夠搭建符合百度排名規(guī)范的網(wǎng)站基底,使您的網(wǎng)站無需額外費(fèi)用,即可穩(wěn)步提升排名至首頁。歡迎體驗(yàn)最佳的哈爾濱網(wǎng)站建設(shè)。
