摘要:作用域和作用域鏈關(guān)于作用域這里不做過多解釋,中根據(jù)作用域可分為全局變量和局部變量。好吧,不是這部分核心,核心是解釋的單線程和事件輪詢機制。這部分就涉及到閉包的理解了。
題目描述前言:這是一道很經(jīng)典的Js面試題,涉及到閉包、變量作用域、setTimeout等知識,對于深入理解這些內(nèi)容很有幫助
//問題描述:請寫出最終的輸出值,并解釋原因 var value1 = 0, value2 = 0, value3 = 0; for ( var i = 1; i <= 3; i++) { var i2 = i; (function() { var i3 = i; setTimeout(function() { value1 += i; value2 += i2; value3 += i3; }, 1); })(); } setTimeout(function() { console.log(value1, value2, value3); }, 100);
//輸出結(jié)果:value1=12; value2=9; value3=6題目解釋
首先,為了下面解釋這道題,我們先來補充一些預(yù)備知識(大神級人物可跳過這部分)
一、基礎(chǔ)知識補充與溫習 1、閉包和作用域官方解釋:所謂“閉包”,指的是一個擁有許多變量和綁定了這些變量的環(huán)境的表達式(通常是一個函數(shù)),因而這些變量也是該表達式的一部分。
function a(){ var i=0; function b(){ alert(++i); } return b; } var c=a(); c();
方言版:當函數(shù)a的內(nèi)部函數(shù)b被外部變量c引用時,就形成了閉包
閉包的作用:我們知道,js里定義在函數(shù)內(nèi)部的是局部變量,外部是無法直接訪問的,而它的內(nèi)部函數(shù)可以訪問,那么內(nèi)部函數(shù)返回一個值,就相當于類似在外部也能訪問局部變量。
閉包的特點:為了使b中能夠訪問i的值,i不會被內(nèi)存回收,就實現(xiàn)了內(nèi)存常駐。對于理解這道題很重要。
閉包的缺點:內(nèi)部閉包函數(shù)可以訪問外部函數(shù)的變量,所以外部函數(shù)的變量不能被釋放,如果閉包嵌套過多,會導(dǎo)致內(nèi)存占用大,出現(xiàn)內(nèi)存溢出。
作用域和作用域鏈:關(guān)于作用域這里不做過多解釋,js中根據(jù)作用域可分為全局變量和局部變量。而對于作用域鏈的簡單理解,可以認為當一個函數(shù)創(chuàng)建之后,從它的執(zhí)行環(huán)境(當前對象)一直到全局對象建立了一個鏈表,可用的變量都掛載在上面。而函數(shù)需要查找某個變量值時,變回按照從當前直到全局對象來進行查找。
2、事件輪詢與setTimeout簡介:setTimeout是js中常見的一個函數(shù),屬于window下的方法(通常,大家會省略window)
語法:setTimeout(code,millisec) 參數(shù)一為代碼,參數(shù)二為毫秒數(shù)
作用:設(shè)定一個時間, 時間到了之后, 就會執(zhí)行一個指定的函數(shù)或表達式,且只執(zhí)行一次。
好吧,setTimeout不是這部分核心,核心是解釋js的單線程和事件輪詢機制。
我們知道,js是單線程的,也就是說所有的任務(wù)要排隊執(zhí)行。
在js中有同步和異步執(zhí)行——
同步執(zhí)行:是指前一個任務(wù)執(zhí)行完,然后下一個任務(wù)繼續(xù)執(zhí)行,都在主線程里。
異步執(zhí)行:則是把事情放進“任務(wù)隊列”(或叫事件隊里),而不是在主線程中,它們通過事件輪詢(Event Loop)和回調(diào)來實現(xiàn)調(diào)入主線程執(zhí)行。
繼續(xù)回到setTimeout,語法里面的code就是異步執(zhí)行的部分。
關(guān)于setTimeout更詳細的內(nèi)容,可點擊這里學習setTimeout那些事
關(guān)于事件輪詢的學習,請點擊這里理解事件輪詢
上面都是些基礎(chǔ)知識,接下來進入正題。
首先,我們拿到題目,要注意到第一個setTimeout里面匿名函數(shù),這部分其實是放在for循環(huán)之后才會執(zhí)行的,因為它是一個異步執(zhí)行的函數(shù),被放到了事件隊列里最后執(zhí)行。而且,每次setTimeout里面的函數(shù)執(zhí)行時可以近似理解為是一次實例化。
value1
在計算value1時,需要用到i,這里涉及到作用域鏈的知識,最內(nèi)層的函數(shù)沒有i的值,它會沿著鏈式結(jié)構(gòu)一直向上查找,最終發(fā)現(xiàn)i是for循環(huán)執(zhí)行之后的值。此時,i的循環(huán)完成,最后一次i++之后,i已經(jīng)變成了4。這樣,setTimeout執(zhí)行3次實例化,每次i的值是不變的,最終值為value1=4+4+4=12。
value2
類似于value1,在執(zhí)行setTimeout里的函數(shù)時,需要找到i2的值,最終我們找到的是for循環(huán)到第三次時i2=i=3。(i2不會等于4,因為到最后一次i++之后,已經(jīng)不會再進入循環(huán)體了)。所以類似上面,value2的值是3+3+3=9。
value3
這部分就涉及到閉包的理解了。在循環(huán)過程中,通過立即執(zhí)行函數(shù)創(chuàng)建了閉包,每次i3都會被賦予當次循環(huán)時i的值并保存,i3的值依次為1,2,3,最終value3=1+2+3=6。
以上為個人解釋,如有錯誤,還望各位指正。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/86759.html
摘要:函數(shù)式編程前端掘金引言面向?qū)ο缶幊桃恢币詠矶际侵械闹鲗?dǎo)范式。函數(shù)式編程是一種強調(diào)減少對程序外部狀態(tài)產(chǎn)生改變的方式。 JavaScript 函數(shù)式編程 - 前端 - 掘金引言 面向?qū)ο缶幊桃恢币詠矶际荍avaScript中的主導(dǎo)范式。JavaScript作為一門多范式編程語言,然而,近幾年,函數(shù)式編程越來越多得受到開發(fā)者的青睞。函數(shù)式編程是一種強調(diào)減少對程序外部狀態(tài)產(chǎn)生改變的方式。因此,...
摘要:一言以蔽之,閉包,你就得掌握。當函數(shù)記住并訪問所在的詞法作用域,閉包就產(chǎn)生了。所以閉包才會得以實現(xiàn)。從技術(shù)上講,這就是閉包。執(zhí)行后,他的內(nèi)部作用域并不會消失,函數(shù)依然保持有作用域的閉包。 網(wǎng)上總結(jié)閉包的文章已經(jīng)爛大街了,不敢說筆者這篇文章多么多么xxx,只是個人理解總結(jié)。各位看官瞅瞅就好,大神還希望多多指正。此篇文章總結(jié)與《JavaScript忍者秘籍》 《你不知道的JavaScri...
摘要:同步異步回調(diào)傻傻分不清楚。分割線上面主要講了同步和回調(diào)執(zhí)行順序的問題,接著我就舉一個包含同步異步回調(diào)的例子。同步優(yōu)先回調(diào)內(nèi)部有個,第二個是一個回調(diào)回調(diào)墊底。異步也,輪到回調(diào)的孩子們回調(diào),出來執(zhí)行了。 同步、異步、回調(diào)?傻傻分不清楚。 大家注意了,教大家一道口訣: 同步優(yōu)先、異步靠邊、回調(diào)墊底(讀起來不順) 用公式表達就是: 同步 => 異步 => 回調(diào) 這口訣有什么用呢?用來對付面試的...
摘要:同步異步回調(diào)傻傻分不清楚。分割線上面主要講了同步和回調(diào)執(zhí)行順序的問題,接著我就舉一個包含同步異步回調(diào)的例子。同步優(yōu)先回調(diào)內(nèi)部有個,第二個是一個回調(diào)回調(diào)墊底。異步也,輪到回調(diào)的孩子們回調(diào),出來執(zhí)行了。 同步、異步、回調(diào)?傻傻分不清楚。 大家注意了,教大家一道口訣: 同步優(yōu)先、異步靠邊、回調(diào)墊底(讀起來不順) 用公式表達就是: 同步 => 異步 => 回調(diào) 這口訣有什么用呢?用來對付面試的...
閱讀 2901·2021-09-22 15:20
閱讀 2972·2021-09-22 15:19
閱讀 3479·2021-09-22 15:15
閱讀 2412·2021-09-08 09:35
閱讀 2387·2019-08-30 15:44
閱讀 3019·2019-08-30 10:50
閱讀 3752·2019-08-29 16:25
閱讀 1600·2019-08-26 13:55