摘要:閉包確實(shí)是一個(gè)說爛了的概念,校招社招都會(huì)被問到,今天總結(jié)一番。先下定義,閉包是函數(shù)和該函數(shù)的詞法作用域的組合。在這個(gè)栗子里,函數(shù)以及它對(duì)變量的引用就構(gòu)成了閉包。閉包和作用域?qū)τ陂]包和作用域的關(guān)系,我的理解是閉包其實(shí)就是作用域的延伸。
閉包確實(shí)是一個(gè)說爛了的概念,校招社招都會(huì)被問到,今天總結(jié)一番。
先下定義,閉包是函數(shù)和該函數(shù)的詞法作用域的組合。其實(shí)這個(gè)定義是比較教條的,可以直白的理解為閉包是一個(gè)函數(shù),且這個(gè)函數(shù)使用了既沒在它內(nèi)部聲明且不是它的參數(shù)的變量。
舉個(gè)栗子,
function foo() { var a = 2; function bar() { console.log( a ); } return bar; } var baz = foo(); baz(); // 2
按照常理,foo函數(shù)在執(zhí)行完畢之后會(huì)銷毀掉其內(nèi)部的變量a,但是bar函數(shù)內(nèi)部保持著對(duì)a的引用,所以通過調(diào)用foo()把bar的引用賦給了baz,運(yùn)行baz()依然可以打印出a。在這個(gè)栗子里,函數(shù)bar以及它對(duì)變量a的引用就構(gòu)成了閉包。
閉包和作用域對(duì)于閉包和作用域的關(guān)系,我的理解是閉包其實(shí)就是作用域的延伸。
由于在JavaScript中函數(shù)內(nèi)部可以使用函數(shù)外部的變量,所有有時(shí)候會(huì)不知不覺的產(chǎn)生閉包,假如在上面那個(gè)代碼片段中,不允許函數(shù)內(nèi)部使用函數(shù)外部的變量,閉包也就無從談起了。
模擬私有變量和私有方法
var Dog = (function(){ var privateVal = "dog" function doing(val) { console.log(privateVal + " " + val) } return { run: function(){ doing("run") }, bark: function(){ doing("bark") } } })() Dog.run() // dog run Dog.bark() // dog bark
可以看到的是,run和bark這兩個(gè)閉包分享了同一個(gè)詞法作用域,且都引用了私有方法doing。這樣,我們就可以只向外暴露run和bark兩個(gè)公共接口而隱藏私有的變量和方法。
閉包與循環(huán)或許這是面試中出現(xiàn)最多的問題...
for(var i = 1;i <= 5;i++) { setTimeout(function() { console.log(i) }, i*1000) } // 每隔一秒打印一個(gè)6,共打印5次
為什么事與愿違,而不是按照我們所想的依次的間隔1秒打印出1,2,3,4,5呢?首先,這段循環(huán)產(chǎn)生了5個(gè)閉包,而且最重要的是這5個(gè)閉包都處在同一個(gè)作用域中,也就是說它們引用的是同一個(gè)i,當(dāng)for循環(huán)結(jié)束時(shí),i變成了6。所以,5個(gè)匿名函數(shù)執(zhí)行時(shí)會(huì)依次的去打印那同一個(gè)i,所以就打印出了5個(gè)6。
如何解決?
之前也說了讓這5個(gè)閉包處于不同的作用域且讓它們?cè)诟髯缘淖饔糜蛑袚碛兴鼈兏髯缘膇即可。
可以使用自執(zhí)行函數(shù)來創(chuàng)建一個(gè)新的作用域
for(var i = 1;i <= 5;i++) { (function(k){ setTimeout(function() { console.log(k) }, k*1000) })(i) }
在這個(gè)代碼片段中,每一個(gè)setTimeout都處于一個(gè)獨(dú)立的作用域中,且都引用了它們各自的k,并不是指向了外層作用域的i,所以就會(huì)打印出1,2,3,4,5
也可以使用let
for(let i = 1;i <= 5;i++) { setTimeout(function() { console.log(i) }, i*1000) }
for 循環(huán)頭部的 let 不僅將 i 綁定到了 for 循環(huán)的塊中,事實(shí)上它將其重新綁定到了循環(huán)的每一個(gè)迭代中,確保使用上一個(gè)循環(huán)迭代結(jié)束時(shí)的值重新進(jìn)行賦值。
其實(shí)使用let的本質(zhì)是
for(let i = 1;i <= 5;i++) { let i = 上次迭代結(jié)束的i setTimeout(function() { console.log(i) }, i*1000) }
其實(shí)閉包就這么多東西,而且主要是作用域的概念,作用域明白了,閉包也就明白了。
that"s all, thank you.
參考資料
深入理解JavaScript系列-閉包
MDN-閉包
《你不知道的JavaScript-上卷》
「每日一題」JS 中的閉包是什么?
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94942.html
摘要:函數(shù)式編程前端掘金引言面向?qū)ο缶幊桃恢币詠矶际侵械闹鲗?dǎo)范式。函數(shù)式編程是一種強(qiáng)調(diào)減少對(duì)程序外部狀態(tài)產(chǎn)生改變的方式。 JavaScript 函數(shù)式編程 - 前端 - 掘金引言 面向?qū)ο缶幊桃恢币詠矶际荍avaScript中的主導(dǎo)范式。JavaScript作為一門多范式編程語言,然而,近幾年,函數(shù)式編程越來越多得受到開發(fā)者的青睞。函數(shù)式編程是一種強(qiáng)調(diào)減少對(duì)程序外部狀態(tài)產(chǎn)生改變的方式。因此,...
摘要:難怪超過三分之一的開發(fā)人員工作需要一些知識(shí)。但是隨著行業(yè)的飽和,初中級(jí)前端就業(yè)形勢(shì)不容樂觀。整個(gè)系列的文章大概有篇左右,從我是如何成為一個(gè)前端工程師,到各種前端框架的知識(shí)。 為什么 call 比 apply 快? 這是一個(gè)非常有意思的問題。 作者會(huì)在參數(shù)為3個(gè)(包含3)以內(nèi)時(shí),優(yōu)先使用 call 方法進(jìn)行事件的處理。而當(dāng)參數(shù)過多(多余3個(gè))時(shí),才考慮使用 apply 方法。 這個(gè)的原因...
摘要:難怪超過三分之一的開發(fā)人員工作需要一些知識(shí)。但是隨著行業(yè)的飽和,初中級(jí)前端就業(yè)形勢(shì)不容樂觀。整個(gè)系列的文章大概有篇左右,從我是如何成為一個(gè)前端工程師,到各種前端框架的知識(shí)。 為什么 call 比 apply 快? 這是一個(gè)非常有意思的問題。 作者會(huì)在參數(shù)為3個(gè)(包含3)以內(nèi)時(shí),優(yōu)先使用 call 方法進(jìn)行事件的處理。而當(dāng)參數(shù)過多(多余3個(gè))時(shí),才考慮使用 apply 方法。 這個(gè)的原因...
摘要:插件開發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實(shí)現(xiàn)文件分片斷點(diǎn)續(xù)傳。 Vue.js 插件開發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。插....
摘要:本文最早為雙十一而作,原標(biāo)題雙大前端工程師讀書清單,以付費(fèi)的形式發(fā)布在上。發(fā)布完本次預(yù)告后,捕捉到了一個(gè)友善的吐槽讀書清單也要收費(fèi)。這本書便從的異步編程講起,幫助我們?cè)O(shè)計(jì)快速響應(yīng)的網(wǎng)絡(luò)應(yīng)用,而非簡(jiǎn)單的頁(yè)面。 本文最早為雙十一而作,原標(biāo)題雙 11 大前端工程師讀書清單,以付費(fèi)的形式發(fā)布在 GitChat 上。發(fā)布之后在讀者圈群聊中和讀者進(jìn)行了深入的交流,現(xiàn)免費(fèi)分享到這里,不足之處歡迎指教...
閱讀 3348·2023-04-26 02:10
閱讀 2913·2021-10-12 10:12
閱讀 4631·2021-09-27 13:35
閱讀 1550·2019-08-30 15:55
閱讀 1097·2019-08-29 18:37
閱讀 3474·2019-08-28 17:51
閱讀 1990·2019-08-26 13:30
閱讀 1231·2019-08-26 12:09