摘要:原文鏈接在這之前先要了解一下循環中和的區別是函數級作用域或者全局作用域,是塊級作用域看一個例子循環中的邏輯代碼函數下的輸出,全局下的不存在現在我們把換為循環中的邏輯代碼報錯了,不在函數作用域下,當然肯定也不會再全局下因為和的這個區別當然和的
原文鏈接
在這之前先要了解一下
var 是函數級作用域或者全局作用域,let是塊級作用域
看一個例子
function foo() { for (var index = 0; index < array.length; index++) { //..循環中的邏輯代碼 } console.log(index);//=>5 } foo() console.log(index)//Uncaught ReferenceError: index is not defined
foo函數下的index輸出5,全局下的index不存在
現在我們把var 換為let
function foo() { for (let index = 0; index < array.length; index++) { //..循環中的邏輯代碼 } console.log(index)//Uncaught ReferenceError: index is not defined } foo()
報錯了,index不在foo函數作用域下,當然肯定也不會再全局下
因為var和let的這個區別(當然var和let的區別不止于此)所以導致了下面的這個問題
關于var的
const array = [1, 2, 3, 4, 5] function foo() { for (var index = 0; index < array.length; index++) { setTimeout(() => { console.log(index); }, 1000); } } foo()
看下輸出
關于let的
const array = [1, 2, 3, 4, 5] function foo() { for (let index = 0; index < array.length; index++) { setTimeout(() => { console.log(index); }, 1000); } } foo()
看下輸出
因為var和let 在作用域上的差別,所以到這了上面的問題
使用var 定義變量的時候,作用域是在foo函數下,在for循環外部,在整個循環中是全局的,每一次的循環實際上是為index賦值,循環一次賦值一次,5次循環完成,index最后的結果賦值就為5;就是被最終賦值的index,就是5;
let的作用局的塊級作用局,index的作用域在for循環內部,即每次循環的index的作用域就是本次循環,下一次循環重新定義變量index;所以index每次循環的輸出都不同
這里還有另外一個問題,setTimeout,這是一個異步,這就是我們今天要討論的
setTimeout(func,time)是在time(毫秒單位)時間后執行func函數。瀏覽器引擎按順序執行程序,遇到setTimeout會將func函數放到執行隊列中,等到主程序執行完畢之后,才開始從執行隊列(隊列中可能有多個待執行的func函數)中按照time延時時間的先后順序取出來func并執行。即使time=0,也會等主程序運行完之后,才會執行。
一個需求,一個數組array[1,2,3,4,5],循環打印,間隔1秒上面的let是循環打印了12345,但是不是間隔1s打印的,是在foo函數執行1s后,同時打印的
方式一 放棄for循環,使用setIntervalfunction foo(){ let index = 0; const array = [1, 2, 3, 4, 5] const t = setInterval(()=>{ if (index < array.length) { console.log(array[index]); } index++; }, 1000); if (index >= array.length) { clearInterval(t); } } foo()
我們上面說到,當for循環遇到了var,變量index的作用域在foo函數下,循環一次賦值一次,5次循環完成,index最后的結果賦值就為5;就是被最終賦值的index,就是5;
方式二,引入全局變量代碼執行順序是,先同步執行for循環,再執行異步隊列,在for循環執行完畢后,異步隊列開始執行之前,index經過for循環的處理,變成了5。
所以我們引入一個全局變量j,使j在for循環執行完畢后,異步隊列開始執行之前,依然是0,在異步執行時進行累加
var j = 0; for (var index = 0; index < array.length; index++) { setTimeout(() => { console.log(j); j++; }, 1000 * index) }方式三 for循環配合setTimeout(常規思路,不贅述,面試必備技能)
const array = [1, 2, 3, 4, 5] function foo() { for (let index = 0; index < array.length; index++) { setTimeout(() => { console.log(index); }, 1000*index); } } foo()方式四,通過閉包實現
開始討論方式四之前我推薦先閱讀一遍我之前寫過一篇文章
談一談javascript作用域
我們對上面的問題再次分析,for循環同步執行,在for循環內部遇到了setTimeout,setTimeout是異步執行的,所以加入了異步隊列,當同步的for循環執行完畢后,再去執行異步隊列,setTimeout中有唯一的一個參數數index
方式三可行,是因為let是塊級作用域,每次for執行都會創建新的變量index,for循環執行完畢后,異步執行之前,創建了5個獨立的作用域,5個index變量,分別是0,1,2,3,4,相互獨立,互不影響,輸出了預期的結果
如果說每次循環都會生成一個獨立的作用域用來保存index,問題就會得到解決,所以,我們通過閉包來實現
const array = [1, 2, 3, 4, 5] function foo() { for (var index = 0; index < array.length; index++) { function fun(j) { setTimeout(function () { console.log(j); }, 1000 * j); } fun(index) } } foo()
setTimeout中的匿名回調函數中引用了函數fun中的局部變量j,所以當fun執行完畢后,變量j不會被釋放,這就形成了閉包
當然我們可以對此進行一下優化
const array = [1, 2, 3, 4, 5] function foo() { for (var index = 0; index < array.length; index++) { (function(j) { setTimeout(function () { console.log(j); }, 1000 * j); })(index) } } foo()
將foo函數改為匿名的立即執行函數,結果是相同的
總結for循環本身是同步執行的,當在for循環中遇到了異步邏輯,異步就會進入異步隊列,當for循環執行結束后,才會執行異步隊列
當異步函數依賴于for循環中的索引時(一定是存在依賴關系的,不然不會再循環中調動異步函數)要考慮作用域的問題,
在ES6中使用let是最佳的選擇,
當使用var時,可以考慮再引入一個索引來替代for循環中的索引,新的索引邏輯要在異步中處理
也可以使用閉包,模擬實現let
在實際開發過程中,循環調用異步函數,比demo要復雜,可能還會出現if和else判斷等邏輯,具體的我們下次再續
參考
通過for循環每隔兩秒按順序打印出arr中的數字
setTimeOut和閉包
《你不知道的JavaScript》上卷
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/102334.html
一、我們先說說javascript的作用域 ①全局變量-函數體外部進行聲明 ②局部變量-函數體內部進行聲明 1)函數級作用域 javascript語言中局部變量不同于C#、Java等高級語言,在這些高級語言內部,采用的塊級作用域中會聲明新的變量,這些變量不會影響到外部作用域。 而javascript則采用的是函數級作用域,也就是說js創建作用域的單位是函數。 例如: 在C#當中我...
摘要:今天同學去面試,做了兩道面試題全部做錯了,發過來給道典型的面試題前端掘金在界中,開發人員的需求量一直居高不下。 排序算法 -- JavaScript 標準參考教程(alpha) - 前端 - 掘金來自《JavaScript 標準參考教程(alpha)》,by 阮一峰 目錄 冒泡排序 簡介 算法實現 選擇排序 簡介 算法實現 ... 圖例詳解那道 setTimeout 與循環閉包的經典面...
摘要:該對象包含了函數的所有局部變量命名參數參數集合以及,然后此對象會被推入作用域鏈的前端。如果整個作用域鏈上都無法找到,則返回。此時的作用域鏈包含了兩個對象的活動對象和對象。 前端學習:教程&開發模塊化/規范化/工程化/優化&工具/調試&值得關注的博客/Git&面試-前端資源匯總 歡迎提issues斧正:閉包 JavaScript-閉包 閉包(closure)是一個讓人又愛又恨的somet...
摘要:內存回收內存泄漏前言最近在細讀高級程序設計,對于我而言,中文版,書中很多地方一筆帶過,所以用自己所理解的,嘗試細致解讀下。內存回收在談內存泄漏之前,首先,先了解下的內存回收機制。 內存回收 && 內存泄漏 前言:最近在細讀Javascript高級程序設計,對于我而言,中文版,書中很多地方一筆帶過,所以用自己所理解的,嘗試細致解讀下。如有紕漏或錯誤,會非常感謝您的指出。文中絕大部分內容...
學習一門知識,有些內容必須要提前明白,比如在學習js中同步異步的問題前,需要明白,js是單線程的,為什么它得是單線程的呢?現在先從它應用的場景來說,就是用來讓用戶與頁面進行交互的吧。假如有js是多線程的,那在這個線程里面,用戶點擊某個按鈕會增加一個DOM節點,在另一個線程里面,用戶點擊這個按鈕又會刪除一個DOM節點,那么此時js就不知道該聽誰的了。這就是為什么會出現同步異步。假設沒有異步,那么...
閱讀 3487·2021-09-08 10:46
閱讀 1189·2019-08-30 13:17
閱讀 2369·2019-08-30 13:05
閱讀 1212·2019-08-29 15:29
閱讀 2890·2019-08-29 11:31
閱讀 544·2019-08-26 12:13
閱讀 1538·2019-08-26 11:42
閱讀 1847·2019-08-23 18:37