摘要:而閉包卻能阻止這件事情發(fā)生。由于的聲明位置使它擁有涵蓋內(nèi)部作用域的閉包,使得該作用域能夠一直存在,以供在之后進(jìn)行引用。到這里,小菊花課堂之閉包的內(nèi)容就告一段落啦,感謝各位能耐心看到這里。
由于前段時(shí)間項(xiàng)目沒(méi)有那么忙,然后我這人一天不看點(diǎn)啥就非常焦慮,于是二刷《你不知道的JavaScript》,現(xiàn)在讀到閉包,想著看完這一章節(jié),寫點(diǎn)東西也是挺好的,所以有了下面的內(nèi)容,如有不對(duì)的地方,敬請(qǐng)斧正,歡迎探討。
作用域我們一般講到閉包,就會(huì)談到作用域,那么作用域又分為了函數(shù)作用域和塊級(jí)作用域 ,在這里,我們簡(jiǎn)單的介紹一下這兩種作用域。
函數(shù)作用域函數(shù)作用域是指,屬于這個(gè)函數(shù)的全部變量都可以在整個(gè)函數(shù)的范圍內(nèi)使用及服用(事實(shí)上在嵌套的作用域中也可以使用)。
我們先來(lái)看一個(gè)例子
var a = 2; function foo() { var a = 3; console.log(a); // 3 } foo(); console.log(a); // 2
可以看到,這種技術(shù)雖然能解決一些問(wèn)題,但是也會(huì)導(dǎo)致其他的問(wèn)題。首先,必須聲明一個(gè)具名函數(shù)foo(),意味著foo這個(gè)名稱本身“污染”了所在的作用域。其次,必須顯式地(有隱式和顯式的區(qū)別,這里暫且不表)通過(guò)函數(shù)名(foo())調(diào)用這個(gè)函數(shù)才能運(yùn)行其中的代碼。
那么我們有沒(méi)有其他的辦法呢,繼續(xù)往下看。
var a = 2 (function foo() { var a = 3; console.log(a); // 3 })(); console.log(a); // 2
比較一下這兩段代碼。第一段中foo被綁定在所在所用域中,可以直接通過(guò)foo()來(lái)調(diào)用調(diào)用它。第二段中foo被綁定在函數(shù)表達(dá)式自身的函數(shù)中而不是所在作用域中。
塊級(jí)作用域在JavaScript中,并不支持塊作用域,但是我們?yōu)槭裁催€要說(shuō)它,因?yàn)樗娘L(fēng)格在JS開發(fā)中很常見。
在日常的開發(fā)或者學(xué)習(xí)工作中,我們其實(shí)經(jīng)常能見到類似塊作用域,思考以下代碼:
for(var i=0; i<6; i++){ console.log(i); }
在for循環(huán)的頭部直接定義了變量 i,通常是因?yàn)橹幌朐趂or循環(huán)內(nèi)部的上下文中使用 i,而忽略了 i 會(huì)被綁定在外部作用域(函數(shù)或全局)中的事實(shí)。當(dāng)使用var時(shí),它寫在哪里都是一樣的,因?yàn)樗鼈冏罱K都會(huì)屬于外部作用域。
塊作用域是一個(gè)用來(lái)對(duì)之前的最小授權(quán)原則進(jìn)行擴(kuò)展的工具,將代碼從在函數(shù)中隱藏信息擴(kuò)展為在塊中隱藏信息。
繼續(xù)思考下面代碼:
function foo() { var a = 2; function bar() { console.log(a); } return bar; } var baz = foo(); baz(); // 2
看到了嗎,這就是閉包的效果。
函數(shù)bar()的詞法作用域能夠訪問(wèn)foo()的內(nèi)部作用域。然后將bar()函數(shù)本身當(dāng)作一個(gè)值類型進(jìn)行傳遞。
在foo()執(zhí)行后,其返回值賦值給變量baz并調(diào)用baz(),實(shí)際上只是通過(guò)不同的標(biāo)識(shí)符引用調(diào)用了內(nèi)部的函數(shù)bar()。
我們知道,JavaScript引擎有垃圾回收器用來(lái)釋放不再使用的內(nèi)存空間。而閉包卻能阻止這件事情發(fā)生。事實(shí)上內(nèi)部作用域依然存在,而沒(méi)有被回收。
由于bar()的聲明位置使它擁有涵蓋foo()內(nèi)部作用域的閉包,使得該作用域能夠一直存在,以供bar()在之后進(jìn)行引用。
再看下面例子
function foo() { var a = 2; function baz() { console.log(a); // 2 } bar(baz); } function bar(fn) { fn(); // 這是閉包 } foo();
無(wú)論通過(guò)何種手段將內(nèi)部函數(shù)傳遞到所在的詞法作用域外,它都會(huì)持有對(duì)原始定義作用域的引用,無(wú)論在何處執(zhí)行這個(gè)函數(shù)都會(huì)使用閉包。
OK,本質(zhì)上無(wú)論何時(shí)何地,如果將(訪問(wèn)它們各自詞法作用域的)函數(shù)當(dāng)作第一級(jí)的值類型并到處傳遞,你就能看到閉包了。在定時(shí)器、事件監(jiān)聽器、Ajax請(qǐng)求或其他異步或同步任務(wù)中,只要使用了回調(diào)函數(shù),實(shí)際上就是在使用閉包。
循環(huán)與閉包先看看最常見的for循環(huán)。
for(var i=1; i<=5; i++) { setTimeout(function timer(){ console.log(i); }, i*1000) }
你覺(jué)得最后會(huì)輸出什么,每秒輸出一次,分別輸出1~5?
那就錯(cuò)啦,實(shí)際上,它是會(huì)每秒輸出一次,但輸出~對(duì),就是66666。
為什么?
延遲函數(shù)的回調(diào)會(huì)在循環(huán)結(jié)束時(shí)才執(zhí)行,而循環(huán)結(jié)束的條件就是i不再<=5。當(dāng)定時(shí)器運(yùn)行時(shí),即使每個(gè)迭代中執(zhí)行的是setTimeout(...,0),所有的回調(diào)函數(shù)依然是在循環(huán)結(jié)束后才被執(zhí)行,所以每次都輸出6。
根據(jù)作用域的原理,盡管循環(huán)中的五個(gè)函數(shù)是在各個(gè)迭代中分別定義的,但是它們都被封閉在一個(gè)共享的全局作用域中,因此只有一個(gè)i。
知道原因之后,我們可以對(duì)代碼進(jìn)行一些改造,看看有沒(méi)有好事發(fā)生。
foo(var i=1; i<=5; i++) { (function (j) { setTimeout(function timer() { console.log(j); }, j*1000); })(i); }
Fine,我們終于改造好了,擁有了更多的詞法作用域。在迭代中使用立即執(zhí)行函數(shù)(IIFE)會(huì)為每個(gè)迭代都生成一個(gè)新的作用域,使得延遲函數(shù)的回調(diào)可以將新的作用域封閉在每個(gè)迭代內(nèi)部,每個(gè)迭代中都會(huì)有一個(gè)具有正確值的變量。
到這里,小菊花課堂之JavaScript閉包的內(nèi)容就告一段落啦,感謝各位能耐心看到這里。
此時(shí)是0點(diǎn)52分,時(shí)候也不早了,該洗洗睡啦。
see u ~ again
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/98801.html
摘要:另外,的綁定和函數(shù)聲明的位置沒(méi)有任何關(guān)系,之取決于函數(shù)的調(diào)用方式。請(qǐng)看下面代碼這樣,我們就可以在調(diào)用的時(shí)候強(qiáng)制把它的綁定到上綁定在傳統(tǒng)的面向類語(yǔ)言中,使用初始化類時(shí)會(huì)調(diào)用類中的構(gòu)造函數(shù)。 關(guān)于this 上一章我們講了關(guān)于作用域和閉包的相關(guān)知識(shí),現(xiàn)在開始新一輪的學(xué)習(xí),那就是JavaScript中最復(fù)雜的機(jī)制之一---this關(guān)鍵字。它是一個(gè)很特別的關(guān)鍵字,被自動(dòng)定義在所有函數(shù)的作用域中。...
摘要:文章來(lái)源詳談防抖和節(jié)流輕松理解函數(shù)節(jié)流和函數(shù)防抖函數(shù)防抖和節(jié)流好啦,今天的小菊花課堂之的防抖與節(jié)流的內(nèi)容就告一段落啦,感各位能耐心看到這里。 前言 陸游有一首《冬夜讀書示子聿》——古人學(xué)問(wèn)無(wú)遺力,少壯工夫老始成。紙上得來(lái)終覺(jué)淺,絕知此事要躬行。,其中的意思想必大家都能明白,在學(xué)習(xí)或工作中,不斷的印證著這首詩(shī)的內(nèi)涵。所以,又有了此篇小菊花文章。 詳解 在前端開發(fā)中,我們經(jīng)常會(huì)碰到一些會(huì)持...
摘要:文章來(lái)源詳談防抖和節(jié)流輕松理解函數(shù)節(jié)流和函數(shù)防抖函數(shù)防抖和節(jié)流好啦,今天的小菊花課堂之的防抖與節(jié)流的內(nèi)容就告一段落啦,感各位能耐心看到這里。 前言 陸游有一首《冬夜讀書示子聿》——古人學(xué)問(wèn)無(wú)遺力,少壯工夫老始成。紙上得來(lái)終覺(jué)淺,絕知此事要躬行。,其中的意思想必大家都能明白,在學(xué)習(xí)或工作中,不斷的印證著這首詩(shī)的內(nèi)涵。所以,又有了此篇小菊花文章。 詳解 在前端開發(fā)中,我們經(jīng)常會(huì)碰到一些會(huì)持...
摘要:的變量作用域是基于其特有的作用域鏈的。需要注意的是,用創(chuàng)建的函數(shù),其作用域指向全局作用域。所以,有另一種說(shuō)法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。 作用域 定義 在編程語(yǔ)言中,作用域控制著變量與參數(shù)的可見性及生命周期,它能減少名稱沖突,而且提供了自動(dòng)內(nèi)存管理 --javascript 語(yǔ)言精粹 我理解的是,一個(gè)變量、函數(shù)或者成員可以在代碼中訪問(wèn)到的范圍。 js的變量作...
摘要:的分句會(huì)創(chuàng)建一個(gè)塊作用域,其聲明的變量?jī)H在中有效。而閉包的神奇作用是阻止此事發(fā)生。依然持有對(duì)該作用域的引用,而這個(gè)引用就叫做閉包。當(dāng)然,無(wú)論使用何種方式對(duì)函數(shù)類型的值進(jìn)行傳遞,當(dāng)函數(shù)在別處被調(diào)用時(shí)都可以觀察到閉包。 date: 16.12.8 Thursday 第一章 作用域是什么 LHS:賦值操作的目標(biāo)是誰(shuí)? 比如: a = 2; RHS:誰(shuí)是賦值操作的源頭? 比如: conso...
閱讀 3793·2021-11-24 09:39
閱讀 2687·2019-08-30 15:54
閱讀 1230·2019-08-30 13:01
閱讀 3521·2019-08-28 18:30
閱讀 1690·2019-08-26 17:44
閱讀 3651·2019-08-26 11:31
閱讀 2497·2019-08-26 10:40
閱讀 1350·2019-08-26 10:27