注:本人發(fā)布的所有文章均為原創(chuàng)吩谦,未經(jīng)作者許可,切勿轉(zhuǎn)載膝藕,謝謝式廷。
本文面向初學(xué)者,大神輕噴芭挽。
- 閉包是什么滑废?
初學(xué)javascript的人,都會(huì)接觸到一個(gè)東西叫做閉包袜爪,聽起來感覺很高大上的蠕趁。網(wǎng)上也有各種五花八門的解釋,其實(shí)我個(gè)人感覺辛馆,沒必要用太理論化的觀念來看待閉包俺陋。
事實(shí)上,你每天都在用閉包怀各,只是你不知道罷了倔韭。
比如:
var cheese = '奶酪';
var test = function(){
alert(cheese);
}
OK,你已經(jīng)寫了一個(gè)閉包瓢对。
- 函數(shù)也是一個(gè)數(shù)據(jù)類型
變量 cheese 是在全局作用域中的一個(gè)變量寿酌,當(dāng)你創(chuàng)建了一個(gè) test 函數(shù),那么硕蛹,test 和 cheese 就共享一個(gè)全局作用域醇疼。
你要額外明白的一點(diǎn)是,在js中法焰,函數(shù)和變量本質(zhì)上是一個(gè)東西秧荆。函數(shù)也是一個(gè)數(shù)據(jù)類型。
從上面的定義中也能看出來這一點(diǎn)埃仪。你要是不相信的話乙濒,我們來看一下咯。
alert(cheese);
alert(test);
讓我們?cè)賮砜纯?test 和 cheese各是什么類型:
alert(typeof test);
alert(typeof cheese);
看到了吧卵蛉,只是類型不同而已颁股,他們都是數(shù)據(jù)類型。
唯一的不同點(diǎn)就是傻丝,函數(shù)類型的 test 可以擁有自己內(nèi)部邏輯甘有,而string類型的 cheese 只能存放一個(gè)字面值,這就是區(qū)別葡缰,僅此而已亏掀。
一目了然了忱反,唯一不同的就是普通變量是字面值一樣的存在,而函數(shù)需要打個(gè)括號(hào)才能執(zhí)行而已滤愕。
你看温算,我現(xiàn)在打一個(gè)括號(hào):
test();
打了括號(hào),才會(huì)執(zhí)行函數(shù)里面的邏輯该互。
- 作用域
讓我們回到閉包米者,現(xiàn)在將之前的代碼做一個(gè)小小的變動(dòng):
var cheese = '奶酪';
var test = function(){
alert(cheese);
}
function test2(){
var cheese = null;
test();
}
test2();
那么韭畸,你覺得現(xiàn)在 alert 出來的是 null 還是奶酪呢宇智?
思考一下。胰丁。随橘。
對(duì)的,彈出來的還是奶酪锦庸。
之前已經(jīng)說過了机蔗,函數(shù) test 和 變量 cheese 同處于一片藍(lán)天下 -- 同一個(gè)作用域。
函數(shù) test 和 變量 cheese 共同享有的作用域叫做全局作用域甘萧,就好像地球一樣萝嘁,我們所有的人都享有這個(gè)地球,能夠在這里呼吸扬卷,吃飯牙言,玩耍。
對(duì)test而言怪得,他能訪問到的作用域只有它本身的閉包和全局作用域:
也就是說咱枉,正常情況下他訪問不到其他閉包里的內(nèi)容,在 test2 里面定義的變量跟它沒有半毛錢關(guān)系徒恋,所以彈出來的 cheese 依舊是全局作用域里的 cheese蚕断。
函數(shù)可以創(chuàng)造自己的作用域。
我們剛才定義了一個(gè) test 函數(shù)入挣,{ } 包裹起來的部分就形成了一個(gè)新的作用域亿乳,也就是所謂的閉包。
其實(shí)你深刻了解了作用域的原理后径筏,閉包也就理解了葛假。
就好比地球是一個(gè)全局作用域,你自己家的房子是一個(gè)函數(shù)匠璧,你的房子是私人空間桐款,就是一個(gè)局部作用域,也就是你自己建了一個(gè)閉包夷恍!
你透過窗戶可以看見外邊的景色魔眨,比如院子里的一棵芭蕉樹媳维,你于是通過眼鏡觀察看到了芭蕉樹的顏色,高度遏暴,枝干的粗細(xì)等等侄刽。
這一棵芭蕉樹相當(dāng)于一個(gè)全局變量,你在自己的閉包內(nèi)可以訪問到它的數(shù)據(jù)朋凉。
所以州丹,在這個(gè)例子中,test 就是一個(gè)房子杂彭,在里面可以通過窗戶訪問到全局作用域中的奶酪 —— 變量 cheese墓毒。
也就是說,cheese 在被 test 訪問到的時(shí)候亲怠,就進(jìn)入了它的閉包所计。
這樣解釋,你是否覺得好理解一點(diǎn)呢团秽?
現(xiàn)在你是否可以理解一開始我說主胧,閉包這東西其實(shí)我們天天都在用的意思了呢?
我們給出閉包的第一個(gè)注解:
1. 閉包就是在函數(shù)被創(chuàng)建的時(shí)候习勤,存在的一個(gè)私有作用域踪栋,并且能夠訪問所有的父級(jí)作用域。
回到剛才的例子:
var cheese = '奶酪';
var test = function(){
alert(cheese);
}
function test2(){
var cheese = null;
test();
}
在這個(gè)例子中图毕,test 和 test2 各自享有一個(gè)作用域夷都,對(duì)不對(duì)?而且他們互相不能訪問吴旋。比如损肛,我在 test 中定義的一個(gè)變量,test2就無法直接訪問荣瑟。
var test = function(){
var i = 10;
}
function test2(){
alert(i);
}
test2();
像這樣治拿,一旦執(zhí)行 test2 函數(shù),編譯就不通過笆焰,因?yàn)樵?test2的閉包內(nèi)劫谅,根本找不到變量 i 。它首先會(huì)在自己的閉包內(nèi)尋找 i嚷掠,找不到的話就去父級(jí)作用域里找捏检,這邊的父級(jí)就是全局作用域,很遺憾不皆,還是沒有贯城。這就是所謂的作用域鏈,它會(huì)一級(jí)一級(jí)往上找霹娄。如果找到最頂層能犯,還是找不到的話鲫骗,就會(huì)報(bào)錯(cuò)了。
在這里踩晶,還有一個(gè)需要注意的點(diǎn)就是:如果某一個(gè)閉包中對(duì)全局作用域(或父級(jí)作用域)中的變量進(jìn)行了修改执泰,那么任何引用該變量的閉包都會(huì)受到牽連。
這的確是一個(gè)需要注意的地方渡蜻。
舉個(gè)例子
var cheese = '奶酪';
var test = function(){
cheese = '奶酪被偷吃了术吝!'
}
function test2(){
alert(cheese);
}
test();
test2();
結(jié)果是:
很有趣,是不是呢茸苇?
當(dāng)我們?cè)诙x一個(gè)函數(shù)排苍,就產(chǎn)生了一個(gè)閉包,如果這個(gè)函數(shù)里面又有若干的內(nèi)部函數(shù)税弃,就是閉包嵌套著閉包纪岁。
像這樣:
function house(){
var footBall = '足球';
/* 客廳 */
function livingRoom(){
var table = '餐桌';
var sofa = '沙發(fā)';
alert(footBall);
}
/* 臥室 */
function bedRoom(){
var bed = '大床';
}
livingRoom();
}
house();
函數(shù)house是一個(gè)閉包,里面又定義了兩個(gè)函數(shù)则果,分別是livingRoom客廳,和bedRoom臥室漩氨,它們各自形成一個(gè)自己的閉包西壮。對(duì)它們而言,父級(jí)作用域就是house叫惊。
如果我們希望在客廳里踢足球款青,在livingRoom函數(shù)執(zhí)行的時(shí)候,它會(huì)先在自己的閉包中找足球霍狰,如果沒找到抡草,就去house里面找。一層一層往上找蔗坯,直至找到了為止谣辞。當(dāng)然澎现,這個(gè)例子可能不是很恰當(dāng)。但起碼展示了作用域,閉包之間的聯(lián)系擂啥。
再說明一下, 閉包就是在函數(shù)被創(chuàng)建的時(shí)候嘀掸,存在的一個(gè)私有作用域涯竟,并且能夠訪問所有的父級(jí)作用域。因此卸奉,從理論上講钝诚,任何函數(shù)都是一個(gè)閉包!
本章結(jié)束 ...
剽悍一小兔榄棵,電氣自動(dòng)化畢業(yè)凝颇。
參加工作后對(duì)計(jì)算機(jī)感興趣郎嫁,深知初學(xué)編程之艱辛。
希望將自己所學(xué)記錄下來祈噪,給初學(xué)者一點(diǎn)幫助泽铛。
免責(zé)聲明: 博客中所有的圖片素材均來自百度搜索,僅供學(xué)習(xí)交流辑鲤,如有問題請(qǐng)聯(lián)系我盔腔,侵立刪,謝謝月褥。