摘要:談?wù)勛约簩?duì)下面這道題目的理解問題這段代碼的輸出是三次,與預(yù)想的,,的輸出不符。此外,還可以使用下面這種方式這里可以使用閉包的知識(shí)進(jìn)行解釋有關(guān)閉包的內(nèi)容可以參見文末的參考鏈接,也可以用作用域輔助理解。
問題談?wù)勛约簩?duì)下面這道題目的理解
for (var i = 1; i <= 3; i++) { setTimeout( function timer() { console.log(i); }, i * 1000 ); }
這段代碼的輸出是三次 4,與預(yù)想的 1,2,3 的輸出不符。以下解釋這一輸出的原因。
分析我們可以將 setTimeout 的第一個(gè)參數(shù) timer() 多帶帶寫出來,變成如下代碼:
for (var i = 1; i <= 3; i++) { function timer() { console.log(i); } setTimeout( timer, i * 1000 ); }
然后我們將循環(huán)展開,三次執(zhí)行過程的變化如下:
// 第一步: i = 1; setTimeout( timer, 1 * 1000 ); // 第二步:i = 2; setTimeout( timer, 2 * 1000 ); // 第三步 i = 3; setTimeout( timer, 3 * 1000 );
注意,在循環(huán)過程中,timer() 函數(shù)并未變化,也沒有執(zhí)行( 計(jì)時(shí)器還未開始 )。
由于 JavaScript 中使用 var i = xxx 聲明的變量是函數(shù)級(jí)別( 而非塊級(jí) )的作用域,因而在 for 循環(huán)條件中聲明的 i 在 for 循環(huán)塊之外的最后一個(gè)函數(shù)體內(nèi)仍是可以訪問的,循環(huán)可以展開為:
var i = 4; function timer() { console.log(i); } setTimeout( timer, 1 * 1000 ); setTimeout( timer, 2 * 1000 ); setTimeout( timer, 3 * 1000 );
因而當(dāng)計(jì)時(shí)器開始的 1s, 2s, 3s 后,timer 會(huì)分別執(zhí)行,此時(shí)會(huì)輸出三次 4。
解決方法若要其每隔 1s 分別輸出 1, 2, 3,可以將 var i = 1 修改為 let i = 1,即:
for (let i = 1; i <= 3; i++) { function timer() { console.log(i); } setTimeout( timer, i * 1000 ); }
注意,由于 let 屬于 ES6 的語法,請(qǐng)注意測(cè)試使用的瀏覽器。
此時(shí),由于 let i = xxx 為塊級(jí)別作用域,因而這一情況下的循環(huán)展開結(jié)果為:
{ let i = 1; setTimeout( timer, 1 * 1000 ); } { let i = 2; setTimeout( timer, 2 * 1000 ); } { let i = 3; setTimeout( timer, 3 * 1000 ); }
注意:這里的 {} 僅用來強(qiáng)調(diào)塊級(jí)別作用域。
此時(shí)便可以得到我們想要的輸出結(jié)果了。
此外,還可以使用下面這種方式:
for (var i = 1; i <= 3; i++) { (function(count){ setTimeout( function timer() { console.log(count); }, count * 1000 ); })(i) }
這里可以使用閉包的知識(shí)進(jìn)行解釋( 有關(guān)閉包的內(nèi)容可以參見文末的參考鏈接 ),也可以用作用域輔助理解。
由于 var i = xxx 是函數(shù)級(jí)別作用域,這里通過一個(gè)立即函數(shù)將變量 i 傳入其中,使其包含在這一函數(shù)的作用域中。而在每次循環(huán)中,此立即函數(shù)都會(huì)將傳入的 i 值保存下來,因而其循環(huán)展開結(jié)果為:
(function(){ var count = 1; setTimeout( function timer() { console.log(count); }, count * 1000 ); })() (function(){ var count = 2; setTimeout( function timer() { console.log(count); }, count * 1000 ); })() (function(){ var count = 3; setTimeout( function timer() { console.log(count); }, count * 1000 ); })()
自然也會(huì)得到我們想要的輸出結(jié)果。
擴(kuò)展 - 塊級(jí)作用域和函數(shù)級(jí)作用域可以用以下代碼進(jìn)行解釋:
{ let i = 2; // 輸出 2 console.log(i); } // 報(bào)錯(cuò):Uncaught ReferenceError: i is not defined console.log(i);
function test(){ // 由于變量提升,輸出 undefined console.log(a); { var a = 1; } // 輸出 1 console.log(a); } // 按照函數(shù)內(nèi)的注釋輸出 test(); // 報(bào)錯(cuò):Uncaught ReferenceError: a is not defined console.log(a);
注:const 聲明的常量與 let 相同,也為塊級(jí)作用域。
參考for 循環(huán)中的...問題,為什么改 var 為 let 就可以解決? - segmentfault
ES6之let(理解閉包)和const命令 - 博客園
「每日一題」JS 中的閉包是什么? - 知乎專欄
前端基礎(chǔ)進(jìn)階(四):詳細(xì)圖解作用域鏈與閉包 - 簡(jiǎn)書
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/81514.html
摘要:同步異步回調(diào)傻傻分不清楚。分割線上面主要講了同步和回調(diào)執(zhí)行順序的問題,接著我就舉一個(gè)包含同步異步回調(diào)的例子。同步優(yōu)先回調(diào)內(nèi)部有個(gè),第二個(gè)是一個(gè)回調(diào)回調(diào)墊底。異步也,輪到回調(diào)的孩子們回調(diào),出來執(zhí)行了。 同步、異步、回調(diào)?傻傻分不清楚。 大家注意了,教大家一道口訣: 同步優(yōu)先、異步靠邊、回調(diào)墊底(讀起來不順) 用公式表達(dá)就是: 同步 => 異步 => 回調(diào) 這口訣有什么用呢?用來對(duì)付面試的...
摘要:同步異步回調(diào)傻傻分不清楚。分割線上面主要講了同步和回調(diào)執(zhí)行順序的問題,接著我就舉一個(gè)包含同步異步回調(diào)的例子。同步優(yōu)先回調(diào)內(nèi)部有個(gè),第二個(gè)是一個(gè)回調(diào)回調(diào)墊底。異步也,輪到回調(diào)的孩子們回調(diào),出來執(zhí)行了。 同步、異步、回調(diào)?傻傻分不清楚。 大家注意了,教大家一道口訣: 同步優(yōu)先、異步靠邊、回調(diào)墊底(讀起來不順) 用公式表達(dá)就是: 同步 => 異步 => 回調(diào) 這口訣有什么用呢?用來對(duì)付面試的...
摘要:異步函數(shù)是值通過事件循環(huán)異步執(zhí)行的函數(shù),它會(huì)通過一個(gè)隱式的返回其結(jié)果。 async 異步函數(shù) 不完全使用攻略 前言 現(xiàn)在已經(jīng)到 8012 年的尾聲了,前端各方面的技術(shù)發(fā)展也層出不窮,VueConf TO 2018 大會(huì) 也發(fā)布了 Vue 3.0的計(jì)劃。而在我們(我)的日常中也經(jīng)常用 Vue 來編寫一些項(xiàng)目。那么,就少不了 ES6 的登場(chǎng)了。那么話說回來,你真的會(huì)用 ES6 的 asyn...
摘要:結(jié)尾有關(guān)這四道經(jīng)典的指針筆試題講解就到此結(jié)束了,如果覺得文章對(duì)自己有所幫助,歡迎大家多多點(diǎn)贊收藏 ?前言 : 今天博主來講解4道經(jīng)典的指針筆試題,很多朋友沒有深刻理...
閱讀 4083·2023-04-26 01:48
閱讀 3258·2021-10-13 09:40
閱讀 1739·2021-09-26 09:55
閱讀 3614·2021-08-12 13:23
閱讀 1780·2021-07-25 21:37
閱讀 3432·2019-08-30 15:53
閱讀 1393·2019-08-29 14:16
閱讀 1397·2019-08-29 12:59