摘要:所以所有函數(shù)共享一個(gè)的引用時(shí),循環(huán)結(jié)構(gòu)讓我們誤認(rèn)為背后還有更復(fù)雜的機(jī)制在器作用,但實(shí)際上啥都木有,如果將延遲函數(shù)的回調(diào)重復(fù)定義五次,完全不使用循環(huán),那他同這段代碼是完全等價(jià)的。
想要說(shuō)明閉包,for循環(huán)是最常見的例子:
for(var i=1;i<=5;i++) { setTimeout(function timer(){ console.log(i); },i*1000); }
以我們所想,我們可能認(rèn)為他會(huì)輸出1~5,每秒一次,每次一個(gè)。
但實(shí)際上,這段代碼在運(yùn)行時(shí)會(huì)以每秒一次的頻率輸出五次6。
這是為什么?
原因是延遲函數(shù)會(huì)在循環(huán)結(jié)束時(shí)才執(zhí)行,事實(shí)上,當(dāng)定時(shí)器運(yùn)行時(shí)即使每個(gè)迭代中執(zhí)行的是setTimeout(...,0),所有的回調(diào)函數(shù)依然是在循環(huán)結(jié)束后才會(huì)執(zhí)行,因此會(huì)每次輸出一個(gè)6出來(lái)。
根據(jù)作用域的原理,實(shí)際情況:盡管循環(huán)中的五個(gè)函數(shù)是在各個(gè)迭代中分別定義的,但是他們都被封閉在一個(gè)共享的全局作用域中,因此實(shí)際上只有一個(gè)i。
所以所有函數(shù)共享一個(gè)i的引用時(shí),循環(huán)結(jié)構(gòu)讓我們誤認(rèn)為背后還有更復(fù)雜的機(jī)制在器作用,但實(shí)際上啥都木有,如果將延遲函數(shù)的回調(diào)重復(fù)定義五次,完全不使用循環(huán),那他同這段代碼是完全等價(jià)的。
解決方法如下:
我們先試一下:
for(var i=1;i<5;i++){ (function(){ setTimeout(function timer(){ console.log(i); },i*1000); })(); }
看似可以,但實(shí)際也沒用,雖然這樣寫我們有更多詞法作用域了,的確每個(gè)延遲函數(shù)都會(huì)將IIFE在每次迭代中創(chuàng)建的作用域封閉起來(lái)。
如果作用域是空的,那么僅僅將他們進(jìn)行封閉是不夠的。仔細(xì)看一下,我們的IIFE只是一個(gè)什么都沒有的空作用域,所以需要包含一點(diǎn)實(shí)際內(nèi)容為我們所用。
他需要自己的變量,用來(lái)在每個(gè)迭代中存儲(chǔ)i的值:
for(var i=0;i<=5;i++) { (function(){ var j=i; setTimeout(function timer(){ console.log(j); },j*1000); })(); }
ok,他運(yùn)行如我們所愿了!
可以進(jìn)行改進(jìn):
for(var i=1;i<=5;i++) { (function{ setTimeout(function timer(){ console.log(j); },j*1000); })(i); //i可以改動(dòng),只要你喜歡 }
在迭代內(nèi)使用IIFE會(huì)為每個(gè)迭代都生成一個(gè)新的作用域,使得延遲函數(shù)的回調(diào)可以將新的作用域封閉在每個(gè)迭代內(nèi)部,每個(gè)迭代中都會(huì)包含一個(gè)具有正確值的變量供我們?cè)L問(wèn)。
使用let解決for循環(huán)的let聲明還會(huì)有一個(gè)特殊行為,這個(gè)行為之處變量在循環(huán)過(guò)程中不知被聲明一次,每次迭代都會(huì)聲明,隨后的每個(gè)迭代都會(huì)使用上一個(gè)迭代結(jié)束時(shí)的值來(lái)初始化這個(gè)變量。
for(var i=1;i<=5;i++) { let j=i; //閉包 setTimeout(function timer(){ console.log(j); },j*1000); } 下面是進(jìn)化版 for(let i;i<=5;i++) { setTimeout(function timer(){ console.log(i); },i*1000); }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94919.html
摘要:執(zhí)行出來(lái)的結(jié)果是這樣的實(shí)驗(yàn)發(fā)現(xiàn),無(wú)論如何都在最后執(zhí)行,這證實(shí)了我們之前遇到的問(wèn)題,因?yàn)樵谘h(huán)結(jié)束才執(zhí)行,所以回調(diào)函數(shù)調(diào)用的取值必然是循環(huán)的最后一次。 前言 https://developer.mozilla.org/zh-CN/docs/JavaScript/Guide/Closures MDN上描述閉包的章節(jié)闡述了一個(gè)由于閉包產(chǎn)生的常見錯(cuò)誤,代碼片段是這樣的 for (var i...
摘要:閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。立即執(zhí)行函數(shù)立即執(zhí)行函數(shù),顧名思義,立即會(huì)執(zhí)行的函數(shù),即當(dāng)讀取到該函數(shù),會(huì)立即執(zhí)行。特性使用語(yǔ)句聲明一個(gè)變量,該變量的范圍限于聲明它的塊中。使用聲明的變量,在聲明前無(wú)法使用,否則將會(huì)導(dǎo)致錯(cuò)誤。 let和閉包 之前一直模模糊糊記得,let解決了某個(gè)閉包問(wèn)題,想用時(shí)又不敢肯定,今天終于遇到這個(gè)問(wèn)題了,那我們就一起來(lái)分析一下,什么是let,let有...
摘要:權(quán)威指南第版中閉包的定義函數(shù)對(duì)象可以通過(guò)作用域鏈相互關(guān)聯(lián)起來(lái),函數(shù)體內(nèi)部的變量都可以保存在函數(shù)作用域內(nèi),這種特性在計(jì)算機(jī)科學(xué)文獻(xiàn)中成為閉包。循環(huán)中的閉包使用閉包時(shí)一種常見的錯(cuò)誤情況是循環(huán)中的閉包,很多初學(xué)者都遇到了這個(gè)問(wèn)題。 閉包簡(jiǎn)介 閉包是JavaScript的重要特性,那么什么是閉包? 《JavaScript高級(jí)程序設(shè)計(jì)(第3版)》中閉包的定義: 閉包就是指有權(quán)訪問(wèn)另一個(gè)函數(shù)中的變...
摘要:因?yàn)闆]有塊級(jí)作用域,只有函數(shù)作用域,所以閉包的使用與函數(shù)是緊密相關(guān)的。模擬私有變量這里返回兩個(gè)閉包函數(shù)和。閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。 簡(jiǎn)介 Javascript 中一個(gè)最重要的特性就是閉包的使用。因?yàn)殚]包的使用,當(dāng)前作用域總可以訪問(wèn)外部的作用域。因?yàn)镴avascript 沒有塊級(jí)作用域,只有函數(shù)作用域,所以閉包的使用與函數(shù)是緊密相關(guān)的。 各種專業(yè)文獻(xiàn)上的閉包(clos...
摘要:中所有的事件綁定都是異步編程當(dāng)前這件事件沒有徹底完成,不再等待,繼續(xù)執(zhí)行下面的任務(wù)當(dāng)綁定事件后,不需要等待執(zhí)行,繼續(xù)執(zhí)行下一個(gè)循環(huán)任務(wù),所以當(dāng)我們點(diǎn)擊執(zhí)行方法的時(shí)候,循環(huán)早已結(jié)束即是最后。 概念 閉包就是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù) 點(diǎn)擊li標(biāo)簽彈出對(duì)應(yīng)數(shù)字 0 1...
閱讀 3679·2021-11-24 09:38
閱讀 3159·2021-11-15 11:37
閱讀 801·2021-11-12 10:36
閱讀 3557·2021-10-21 09:38
閱讀 3230·2021-09-28 09:36
閱讀 2430·2021-09-22 16:01
閱讀 5006·2021-09-22 15:09
閱讀 1230·2019-08-30 15:55