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