最近看了js高級(jí)程序,書(shū)上對(duì)于閉包的解釋是:''閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量''.我覺(jué)得過(guò)于抽象,經(jīng)過(guò)一番查閱折騰,現(xiàn)在來(lái)談?wù)勎易约旱睦斫?希望對(duì)大家有所幫助.
-
談閉包之前,還是先談?wù)勛饔糜?/strong>
①作用域:
在之前的文章中就已經(jīng)提到過(guò),作用域簡(jiǎn)單來(lái)說(shuō)就是某個(gè)變量有(起)作用的范圍;它的規(guī)則是內(nèi)層的作用域可以訪問(wèn)外層的作用域,但是反過(guò)來(lái)不行
function foo(){
var b = "10";
}
console.log(b);//報(bào)錯(cuò),b is not defined
在上面的demo中,全局是訪問(wèn)不到變量b的,外層無(wú)法訪問(wèn)內(nèi)部變量.
②作用域鏈搜索原則:
在作用域中如果訪問(wèn)(讀取|設(shè)置)某個(gè)變量,先在當(dāng)前作用域中搜索,如果找到那么就直接使用;
如果沒(méi)有找到,那么就向上一級(jí)作用域中繼續(xù)搜索,找到則使用,沒(méi)有找到就重復(fù)上面的過(guò)程,直到0級(jí)作用域鏈;
var num = 10;
function f1(){
console.log(num); //undefined 函數(shù)內(nèi)部變量提升,結(jié)果并不是10
num = 66;
console.log(num); // 66
function f2(){
var num = 99;
console.log(num); // 99
}
var num = 1000;
f2();
console.log(num); //1000
}
f1();
console.log(num); //10 只能訪問(wèn)全局變量
了解完作用域的概念之后,對(duì)書(shū)上閉包的解釋相信大家也有了一定的理解了,既然外面作用域不能訪問(wèn)內(nèi)部作用域的變量,那如果我們需要訪問(wèn)的話,該怎么解決這個(gè)問(wèn)題呢?于是,閉包就誕生了.
-
我對(duì)于閉包的理解
我對(duì)于閉包的理解,就是一種能提供一種間接訪問(wèn)封閉空間中私有數(shù)據(jù)的方法,你也可以理解為外部作用域訪問(wèn)內(nèi)部作用域變量的方法.
那么這個(gè)方法是如何實(shí)現(xiàn)的呢?
閉包二字單純來(lái)講,所謂閉,就是指封閉,包,就是指包裹、包裝起來(lái)。JavaScript中沒(méi)有塊級(jí)作用域,函數(shù)是唯一一個(gè)可以創(chuàng)建作用域的對(duì)象,那么想封閉一個(gè)作用域,必須使用函數(shù)來(lái)包裹閉合。
function f1(){
var num = 10;
return function(){
console.log(num);
};
}
var f2=f1();
f2();// 10
上面的案例就是一個(gè)典型的閉包,外面作用域訪問(wèn)了函數(shù)內(nèi)部的變量num,那么這是怎么做到的呢?
代碼中我們?cè)趂1函數(shù)中return了一個(gè)函數(shù)f2,我們調(diào)用f1函數(shù)的結(jié)果為f2函數(shù),在f2函數(shù)調(diào)用時(shí),會(huì)先創(chuàng)建一個(gè)執(zhí)行環(huán)境,以及相應(yīng)的作用域鏈,在函數(shù)執(zhí)行中,為讀取和寫(xiě)入變量的值,就需要在作用域鏈中查找變量,對(duì)于f2函數(shù),f1是它的外部作用域,所以自然能夠訪問(wèn)f1中的變量num,不知不覺(jué)地,全局作用域就訪問(wèn)了f1中變量num的值。
-
閉包和for循環(huán)
《你不知道的JavaScript》中有這樣一個(gè)案例:
for (var i = 1; i <= 5; i++) {
setTimeout( function timer(){
console.log(i);
},i*1000);
}
代碼的運(yùn)行結(jié)果:每秒輸出一次,且輸出了五次6.
setTimeout是傳入了一個(gè)函數(shù),延遲一段時(shí)間把這個(gè)函數(shù)添加到隊(duì)列當(dāng)中,并不是延遲一段時(shí)間之后就執(zhí)行函數(shù),這個(gè)函數(shù)要在所有其他函數(shù)執(zhí)行完畢之后才開(kāi)始執(zhí)行,在這之前,for循環(huán)早已經(jīng)執(zhí)行完畢,所以每次打印的值都是for循環(huán)的最終i值6,且每秒輸出一次。
在這里,定時(shí)器里的函數(shù)無(wú)法獲取其作用域范圍的值(i的值),根本原因是定時(shí)器是一個(gè)異步事件,要解決這個(gè)問(wèn)題,我們?cè)诤瘮?shù)上加上了一個(gè)立即執(zhí)行函數(shù)()();使得每一次循環(huán)的時(shí)候定時(shí)器函數(shù)立即執(zhí)行,獲取i的打印值。
for (var i = 1; i <= 5; i++) {
setTimeout(( function timer(){
console.log(i);//1,2,3,4,5
})(),i*1000);
}
當(dāng)然,Es6中使用let申明變量,申明了塊級(jí)作用域也起到了同樣的作用,這里我沒(méi)有深入了解,暫不解析。
for (let i = 1; i <= 5; i++) {
setTimeout( function timer(){
console.log(i);//1,2,3,4,5
},i*1000);
}
好了,就寫(xiě)這么些,希望大家看了能夠有所收獲,不足之處也歡迎指正,O(∩_∩)O謝謝!