日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

《你不知道的JavaScript》-- 精读(五)

發(fā)布時(shí)間:2024/9/21 javascript 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《你不知道的JavaScript》-- 精读(五) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

知識(shí)點(diǎn)

1.實(shí)質(zhì)問題

當(dāng)函數(shù)可以記住并訪問所在的詞法作用域時(shí),就產(chǎn)生了閉包,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行。

function foo(){var a = 2;function bar(){console.log(a); // 2}bar() } foo() 復(fù)制代碼

根據(jù)前面的定義,嚴(yán)格來說上述代碼并不是閉包,最準(zhǔn)確地用來解釋bar()對(duì)a的引用的方法是詞法作用域的查找規(guī)則,而這些規(guī)則只是閉包的一部分。

function foo(){var a = 2;function bar(){console.log(a);}return bar; } var baz = foo(); baz(); // 2 ---- 這就是閉包的效果 復(fù)制代碼

上述代碼中,在foo()執(zhí)行后,其返回值(也就是內(nèi)部的bar()函數(shù))賦值給變量baz并調(diào)用baz(),實(shí)際上只是通過不同的標(biāo)識(shí)符引用調(diào)用了內(nèi)部的函數(shù)bar()。

bar()顯然可以被正常執(zhí)行。但是在這個(gè)例子中,它在自己定義的詞法作用域以外的地方執(zhí)行。

在foo()執(zhí)行后,通常會(huì)期待foo()的整個(gè)內(nèi)部作用域都被銷毀,因?yàn)槲覀冎酪嬗欣厥掌鱽磲尫挪辉偈褂玫膬?nèi)存空間。而閉包的“神奇”之處正是可以阻止這件事情的發(fā)生。事實(shí)上內(nèi)部作用域依然存在,因此沒有被回收,因?yàn)閎ar()本身在使用。

因?yàn)閎ar()所聲明的位置,它擁有涵蓋foo()內(nèi)部作用域的閉包。使得該作用域一直存活,以供bar()在之后任何時(shí)間進(jìn)行引用。

bar()依然持有對(duì)該作用域的引用,而這個(gè)引用就叫作閉包。

閉包使得函數(shù)可以繼續(xù)訪問定義時(shí)的詞法作用域。當(dāng)然,無論使用何種方式對(duì)函數(shù)類型的值進(jìn)行傳遞,當(dāng)函數(shù)在別處被調(diào)用時(shí)都可以觀察到閉包。

function foo(){var a = 2;function baz(){console.log(a);}bar(baz); } function bar(fn){fn(); // 這就是閉包 } 復(fù)制代碼

把內(nèi)部函數(shù)baz傳遞給bar,當(dāng)調(diào)用這個(gè)內(nèi)部函數(shù)時(shí)(現(xiàn)在叫作fn),它涵蓋的foo()內(nèi)部作用域的閉包就可以觀察到了,因?yàn)樗軌蛟L問a。

傳遞函數(shù)當(dāng)然也可以是間接的。

var fn ; function foo(){var a = 2;function baz(){console.log(a);}fn = baz; // 將baz分配給全局變量 } function bar(){fn(); // 這就是閉包 } 復(fù)制代碼

無論通過何種手段將內(nèi)部函數(shù)傳遞到所在的詞法作用域以外,它都會(huì)持有對(duì)原始定義作用域的引用,無論在何處執(zhí)行這個(gè)函數(shù)都會(huì)使用閉包。

2.現(xiàn)在我懂了

function wait(message){setTimeout(function timer(){console.log(message);},1000) } wait("Hello,closure!") 復(fù)制代碼

將一個(gè)內(nèi)部函數(shù)(名為timer)傳遞給setTimeout(..)。timer具有涵蓋wait(..)作用域的閉包,因此還保有對(duì)變量message的引用。

wait(..)執(zhí)行1000毫秒后,它的內(nèi)部作用域并不會(huì)消失,timer函數(shù)依然保有wait(..)作用域的閉包。

本質(zhì)上,無論何時(shí)何地,如果將(訪問它們各自詞法作用域的)函數(shù)當(dāng)作第一級(jí)的值類型并到處傳遞,你就會(huì)看到閉包在這些函數(shù)中的作用。在定時(shí)器、事件監(jiān)聽器、Ajax請求、跨窗口通信、Web Workers或者任何其他的異步(或者同步)任務(wù)中,只要使用了回調(diào)函數(shù),實(shí)際上就是在使用閉包。

3.循環(huán)和閉包

for(var i = 1;i <= 5; i++){setTimeout(function timer(){console.log(i); // 每秒一次的頻率輸出5次6},i*1000) } 復(fù)制代碼

根據(jù)作用域的工作原理,實(shí)際情況是盡管循環(huán)中的五個(gè)函數(shù)是在各個(gè)迭代中分別定義的,但是它們都被封閉在一個(gè)共享的全局作用域中,因此實(shí)際上只有一個(gè)i。

for(var i = 1; i <= 5; i++){(function(j){setTimeout(function timer(){console.log(j);},j*1000);})(i) } 復(fù)制代碼

在迭代內(nèi)使用IIFE會(huì)為每個(gè)迭代都生成一個(gè)新的作用域,使得延遲函數(shù)的回調(diào)可以將新的作用域封閉在每個(gè)迭代內(nèi)部,每個(gè)迭代中都會(huì)含有一個(gè)具有正確值的變量供我們訪問。

4.重返塊作用域

for(let i = 1; i <= 5; i++){setTimeout(function timer(){console.log(i);},i*1000) } 復(fù)制代碼

5.模塊

function foo(){var something = "cool";var another = [1,2,3];function doSomething(){console.log(something);}function doAnother(){console.log(another.join(" ! ")} } 復(fù)制代碼

私有數(shù)據(jù)變量something和another,以及doSomething()和doAnother()兩個(gè)內(nèi)部函數(shù),它們的詞法作用域(而這就是閉包)也就是foo()的內(nèi)部作用域。

function CoolModule(){var something = "cool";var another = [1,2,3];function doSomething(){console.log(something);}function doAnother(){console.log(another.join("!");}return {doSomething: doSomething,doAnother: doAnother} }var foo = CoolModule(); foo.doSomething(); // cool foo.doAnother(); // 1!2!3 復(fù)制代碼

這個(gè)模式在JavaScript中被稱為模塊。

模塊模式需要具備兩個(gè)必要條件。

1.必須有外部的封閉函數(shù),該函數(shù)必須至少被調(diào)用一次(每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的模塊實(shí)例)。

2.封閉函數(shù)必須返回至少一個(gè)內(nèi)部函數(shù),這樣內(nèi)部函數(shù)才能在私有作用域中形成閉包,并且可以訪問或者修改私有的狀態(tài)。

一個(gè)從函數(shù)調(diào)用所返回的,只有數(shù)據(jù)屬性而沒有閉包函數(shù)的對(duì)象并不是真正的模塊。

var foo = (function CoolModule(){var something = "cool";var another = [1,2,3];function doSomething(){console.log(something);}function doAnother(){console.log(another.join("!");}return {doSomething: doSomething,doAnother: doAnother} })() foo.doSomething(); // cool foo.doAnother(); // 1!2!3 復(fù)制代碼

將模塊函數(shù)轉(zhuǎn)換成了IIFE,立即調(diào)用這個(gè)函數(shù)并將返回值直接賦值給單例的模塊實(shí)例標(biāo)識(shí)符foo。

模塊模式的一個(gè)簡單但強(qiáng)大的用法是命名將要作為公共API返回的對(duì)象:

var foo = (function CoolModule(id){function change(){// 修改公共APIpublicAPI.identify = identify2;}function identify1(){console.log(id);}function identify2(){console.log(id.toUpperCase());}var publicAPI = {change: change,identify: identify1}return publicAPI; }("foo mocule");foo.identify(); // foo module foo.change(); foo.identify(); // FOO MODULE 復(fù)制代碼

通過在模塊實(shí)例的內(nèi)部保留對(duì)公共API對(duì)象的內(nèi)部引用,可以從內(nèi)部對(duì)模塊實(shí)例進(jìn)行修改,包括添加或刪除方法和屬性,以及修改它們的值。

總結(jié)

我們在詞法作用域的環(huán)境下寫代碼,而其中的函數(shù)也是值,可以隨意傳來傳去。

當(dāng)函數(shù)可以記住并訪問所在的詞法作用域,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行,這時(shí)就產(chǎn)生了閉包。

閉包是一個(gè)非常強(qiáng)大的工具,可以用多種形式來實(shí)現(xiàn)模塊等模式。

模塊有兩個(gè)主要特征:

  • 1.為創(chuàng)建內(nèi)部作用域而調(diào)用了一個(gè)包裝函數(shù)
  • 2.包裝函數(shù)的返回值必須至少包括一個(gè)對(duì)內(nèi)部函數(shù)的引用,這樣就會(huì)創(chuàng)建涵蓋整個(gè)包裝函數(shù)內(nèi)部作用域的閉包。

巴拉巴拉

關(guān)于腦子一熱

我的經(jīng)歷告訴我,腦子一熱做的事情,多半會(huì)后悔,而且會(huì)非常后悔。但是怎么去避免呢,方法我還沒找到,每次我遇到這樣的情緒,都會(huì)找各種理由去逃避,這是目前我的低級(jí)應(yīng)對(duì)措施,相當(dāng)?shù)图?jí)。如果能從根源消除是最好不過的了,可是我還沒有那么大的控制力,所以只能慢慢去培養(yǎng),盡量減少這種上頭的次數(shù)了。

轉(zhuǎn)載于:https://juejin.im/post/5d0a363a51882563194b31f2

總結(jié)

以上是生活随笔為你收集整理的《你不知道的JavaScript》-- 精读(五)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。