摘要:本文從使用對數(shù)組進行遍歷開始說起,粗略對比使用進行遍歷的差異,并由此引入中可迭代對象迭代器的概念,并對其進行粗略介紹。說到這里,就繼續(xù)說一下迭代器關(guān)閉的情況了。確實,符合可迭代協(xié)議和迭代器協(xié)議的。
本文從使用 forEach 對數(shù)組進行遍歷開始說起,粗略對比使用 forEach , for...in , for...of 進行遍歷的差異,并由此引入 ES6 中 可迭代對象/迭代器 的概念,并對其進行粗略介紹。
forEachforEach?方法按升序為數(shù)組中的有效值的每一項執(zhí)行一次callback**?函數(shù)特點
只會對“有效值”進行操作,也就是說遍歷的時候會跳過已刪除或者未初始化的項,即會跳過那些值為 empty 的內(nèi)容。(undefined 不會被跳過哦)。
forEach 的第二個參數(shù) thisArg 傳入后可以改變 callback 函數(shù)的 this 值,如果不傳則為 undefined 。當然, callback 所拿到的 this 值也受普遍規(guī)律的支配:這意味著如果 callback 是個箭頭函數(shù),則 thisArg 會被忽略。(因為箭頭函數(shù)已經(jīng)在詞法上綁定了this值,不能再改了)
不建議在循環(huán)中增/刪數(shù)組內(nèi)容:首先,forEach?遍歷的范圍在第一次調(diào)用?callback?前就會確定,這意味著調(diào)用forEach?后添加到數(shù)組中的項不會被?callback?訪問到。同時,由于 forEach 的遍歷是基于下標的(可以這么理解,并能從 Polyfill 中看到這一實現(xiàn)),那么在循環(huán)中刪了數(shù)組幾項內(nèi)容則會有奇怪的事情發(fā)生:比如下圖中,在下標為 1 的時候執(zhí)行了 shift() ,那么原來的第 3 項變?yōu)榱说?2 項,原來的第 2 項則被跳過了。
缺點
無法中斷或跳出:如果想要中斷/跳出,可以考慮使用如下兩種方式:
使用 for / for..of
使用 Array.prototype.every() / Array.prototype.some() 并返回 false / true 來進行中斷/跳出
無法鏈式調(diào)用(因為返回了 undefined )
for...infor...in 循環(huán)實際是為循環(huán) enumerable 對象而設(shè)計的:
for...in 語句以任意順序遍歷一個對象的 可枚舉屬性 。對于每個不同的屬性,語句都會被執(zhí)行。特點
遍歷可枚舉屬性:for...in 循環(huán)將遍歷對象本身的所有可枚舉屬性,以及對象從其構(gòu)造函數(shù)原型中繼承的屬性(更接近原型鏈中對象的屬性覆蓋原型屬性)。
可以被中斷
缺點會訪問到能訪問到的所有的 可枚舉屬性 ,也就是說會包括那些原型鏈上的屬性。如果想要僅迭代自身的屬性,那么在使用 for...in 的同時還需要配合 getOwnPropertyNames() 或 hasOwnProperty()
不能保證for ... in將以任何特定的順序返回索引,比如在 IE 下可能會亂來。
不建議用于迭代 Array:
不一定保證按照下標順序
遍歷得到的下標是字符串而不是數(shù)字
for...offor...of 的本質(zhì)是在 可迭代對象(iterable objects) 上調(diào)用其 迭代方法 創(chuàng)建一個迭代循環(huán),并執(zhí)行對應(yīng)語句??梢缘?數(shù)組/字符串/類型化數(shù)組(TypedArray)/Map/Set/generators/類數(shù)組對象(NodeList/arguments) 等。需要注意的是, Object 并不是一個可迭代對象。
for...of 是 ES6 中新出爐的,其彌補了 forEach 和 for...in 的諸多缺點:
可以使用break,?throw?或return 等終止
能正常迭代數(shù)組(依賴數(shù)組默認的迭代行為)
區(qū)別那么我們簡單的幾句話說明一下 for...of 和 for...in 的區(qū)別:
for...in?語句以原始插入順序(還不一定保證)迭代對象的 可枚舉屬性 。
for...of?語句遍歷 可迭代對象 定義要迭代的數(shù)據(jù)。
for...of ?得到的是 值(value), 而 for...in 得到的是 鍵(key)。
那么扯到了 可迭代對象 ,就不得不說說 ES6 中新增的與 可迭代對象/迭代器 有關(guān)東西了。
可迭代對象/__迭代器__iteration 是 ES6 中新引入的遍歷數(shù)據(jù)的機制,其核心概念是:iterable(可迭代對象)? 和 iterator(迭代器):
iterable(可迭代對象):一種希望被外界訪問的內(nèi)部元素的數(shù)據(jù)結(jié)構(gòu),實現(xiàn)了 Symbol.iterator 方法
iterator(迭代器):用于遍歷數(shù)據(jù)結(jié)構(gòu)元素的指針
可迭代協(xié)議(iterable protocol)首先,可迭代協(xié)議允許 JavaScript 對象去定義或定制它們的迭代行為。而如 Array?或?Map 等內(nèi)置可迭代對象有默認的迭代行為,而如 Object 則沒有。(所以為什么不能對 Object 用 for...of )再具體一點,即對象或其原型上有 [Symbol.iterator] 的方法,用于返回一個對象的無參函數(shù),被返回對象符合迭代器協(xié)議。然后在該對象被迭代的時候,調(diào)用其 [Symbol.iterator] 方法獲得一個在迭代中使用的迭代器。
首先可以看見的是 Array 和 Map 在原型鏈上有該方法,而 Object 則沒有。這印證了上面對于哪些可以用于 for...of ?的說法。
如果我們非要想用 for...of 對 Object 所擁有的屬性進行遍歷,則可使用內(nèi)置的 Object.keys() 方法:
for (const key of Object.keys(someObject)) { console.log(key + ": " + someObject[key]); }
或者如果你想要更簡單得同時得到鍵和值,可以考慮使用 Object.entries() :
for (const [key, value] of Object.entries(someObject)) { console.log(key + ": " + value); }
其次,有如下情況會使用可迭代對象的迭代行為:
for...of
擴展運算符(Spread syntax)
yield*
解構(gòu)賦值(Destructuring assignment)
一些可接受可迭代對象的內(nèi)置API(本質(zhì)是這些接口在接收一個數(shù)組作為參數(shù)的時候會調(diào)用數(shù)組的迭代行為)
Array.from()
Map(), Set(), WeakMap(), WeakSet()
Promise.all()/Promise.race() 等
迭代器協(xié)議(iterator protocol)上文說到返回了一個迭代器用于迭代,那我們就來看看符合什么樣規(guī)范的才算一個 迭代器 。
只需要實現(xiàn)一個符合如下要求的 next 方法的對象即可:
本質(zhì)上,在使用一個迭代器的時候,會不斷調(diào)用其 next() 方法直到返回 done: true 。
自定義迭代行為既然符合可迭代協(xié)議的均為可迭代對象,那接下來就簡單自定義一下迭代行為:
// 讓我們的數(shù)組倒序輸出 value const myArr = [1, 2, 3]; myArr[Symbol.iterator] = function () { const that = this; let index = that.length; return { next: function () { if (index > 0) { index--; return { value: that[index], done: false }; } else { return { done: true }; } }, }; }; [...myArr]; // [3, 2, 1] Array.from(myArr) // [3, 2, 1]一句說明可迭代對象和迭代器的關(guān)系
當一個__可迭代對象__需要被迭代的時候,它的 Symbol.iterator 方法被無參調(diào)用,然后返回一個用于在迭代中獲得值的迭代器。
換句話說,一個對象(或其原型)上有符合標準的 Symbol.iterator 接口,那他就是 可迭代的(Iterator) ,調(diào)用這個接口返回的對象就是一個 迭代器
上文提到說 for...of 比 forEach 好在其可以被“中斷”,那么對于在 for...of 中中斷迭代,其本質(zhì)是中斷了迭代器,迭代器在中斷后會被關(guān)閉。說到這里,就繼續(xù)說一下迭代器關(guān)閉的情況了。
首先,迭代器的關(guān)閉分為兩種情況:
Exhaustion:當被持續(xù)調(diào)用 next() 方法直到返回 done: true ,也就是迭代器正常執(zhí)行完后關(guān)閉
Closing:通過調(diào)用 return() 方法來告訴迭代器不打算再調(diào)用 next() 方法
那么什么時候會調(diào)用迭代器的 return 方法呢:
首先,return() 是個可選方法,只有具有該方法的迭代器才是 可關(guān)閉的(closable)
其次,只有當沒有 Exhaustion 時才應(yīng)該調(diào)用 return() ,如 break,?throw?或 return等
最后,return() 方法中也不是想怎么寫就怎么寫的,也有自己的要求, return()方法需要符合以下規(guī)范:
return(x) 通常應(yīng)該返回如 { done: true, value: x } 的結(jié)果,如果返回的不是個對象則會報錯
調(diào)用return()后,?next()返回的對象也應(yīng)該是 done:true?(這就是為什么有一些迭代器在 for...of 循環(huán)中中斷后無法再次使用的原因,比如 Generator )
同時,需要額外注意的是,及時在收到迭代器最后一個值后調(diào)用 break 等,也會觸發(fā) return()
function createIterable() { let done = false; const iterable = { [Symbol.iterator]() { return this; }, next() { if (!done) { done = true; return { done: false, value: "a" }; } else { return { done: true, value: undefined }; } }, return () { console.log("return() was called!"); return { done: true, value: undefined }; }, }; return iterable; } for (const value of createIterable()) { console.log(value); break; }生成器(Generator) 既是迭代器也是可迭代對象
上文 迭代器協(xié)議 中提到的返回的擁有 next() 方法的對象和我們在 Generator 中使用的 next() 方法似乎一模一樣。確實, Generator 符合可迭代協(xié)議和迭代器協(xié)議的。
因為 Generator 既有符合規(guī)范的 next() (迭代器協(xié)議)方法,也有 Symbol.iterator (可迭代協(xié)議)方法,因此它 既是迭代器也是可迭代對象 。
可關(guān)閉的(closable)默認情況下,Generator對象是可關(guān)閉的。因此在用 for...of 時中斷迭代后,無法再次對原有 Generator對象進行迭代。(因為調(diào)用return()后,?next()返回的對象也應(yīng)該是 done:true)
當然,既然是默認情況,我們就可以想辦法讓其無法被關(guān)閉:
可以通過包裝一下迭代器,將迭代器本身/原型上的 return() 方法被重寫掉
class PreventReturn { constructor(iterator) { this.iterator = iterator; } [Symbol.iterator]() { return this; } next() { return this.iterator.next(); } // 重寫掉 return 方法 return (value = undefined) { return { done: false, value }; } }
更多關(guān)于 Generator 的內(nèi)容就不在本篇進行闡述,有機會將多帶帶作為一篇慢慢講。
參考MDN-forEach
MDN-for...in
MDN-for...of
MDN-delete(跨瀏覽器提示)
MDN-迭代協(xié)議
MDN-Generator
Iterator 和 for...of 循環(huán)
Iterables and iterators
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/98806.html
摘要:將數(shù)組或者集合中的全部或者一部數(shù)據(jù)取出來,用迭代器比較方便迭代器能陸續(xù)遍歷幾個迭代器按順序迭代訪問幾個不同的迭代器。 一、SPL簡介 ?????什么是SPL PHP的標準庫SPL:Standard PHP Library ?????SPL: 用于解決常見普遍問題的一組接口與類的集合 ?????Common Problem: 數(shù)學(xué)建模/數(shù)據(jù)結(jié)構(gòu) 解決數(shù)據(jù)怎么存儲的問題 元素遍歷 ...
摘要:具體原因在后面說明是必須實現(xiàn)的接口,返回了一個迭代器。迭代器,可以對已知集合進行遍歷操作。這里可以看出,循環(huán)最終其實是會使用方法獲取迭代器,來完成遍歷。 概述 迭代器,提供了在不了解集合內(nèi)部實現(xiàn)方法的時候遍歷集合的能力。可以將容器內(nèi)部實現(xiàn)與遍歷操作隔離、解耦。 使用迭代器實現(xiàn)一個簡單集合 通過自定義一個簡單集合,并在對其使用迭代器進行遍歷,達到掌握迭代器的目的。 集合描述 一個簡單的集...
摘要:數(shù)組在中使用度非常頻繁,我總結(jié)了一些在數(shù)組中很常見的問題。否則返回語言類型返回數(shù)組中滿足提供的測試函數(shù)的第一個元素的索引。接受兩個參數(shù)和,代表需要截取的數(shù)組的開始序號和結(jié)束序號。其中表示添加的元素個數(shù)。 數(shù)組在javascript中使用度非常頻繁,我總結(jié)了一些在數(shù)組中很常見的問題。 關(guān)于數(shù)組中的方法非常多,我總結(jié)了一張表來大致了解數(shù)組中的方法 Array中的方法 含義 改變原數(shù)組 ...
摘要:前一個值,當前值,索引,數(shù)組對象產(chǎn)生新數(shù)組的迭代器方法類似,對數(shù)組的每個元素使用某個函數(shù),并返回新數(shù)組和相似,傳入一個返回值為布爾類型的函數(shù)。 1. 前言 數(shù)組真的是每天用了,但是有很多方法都是記不住,總是要百度查,很煩,所以才寫了個數(shù)組使用總結(jié),有什么不對的希望大家指出來。 2. 思路 先看看這些問題都記得很清楚么? 創(chuàng)建數(shù)組,怎么創(chuàng)建數(shù)組的 數(shù)組的構(gòu)造方法Array有哪些方法?E...
摘要:中可以實現(xiàn)遍歷的數(shù)據(jù)類型主要是對象,其中包括普通對象與數(shù)組。遍歷器是一種接口,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機制。實例五遍歷器對象實例五是的遍歷過程,通過手動調(diào)用其對象的方法實現(xiàn)信息獲取。為每個數(shù)組元素執(zhí)行函數(shù)。 前言 ??將依據(jù)自身痛點學(xué)習,計劃對原生JavaScript寫一個系統(tǒng),本文為第一篇,感興趣的同學(xué)可以關(guān)注個人公眾號:ZeroToOneMe,或者github博客,將持續(xù)...
閱讀 982·2023-04-25 23:54
閱讀 3059·2021-11-08 13:21
閱讀 3810·2021-09-27 13:35
閱讀 3408·2021-07-26 23:41
閱讀 1069·2019-08-30 15:52
閱讀 3456·2019-08-30 11:27
閱讀 2115·2019-08-29 18:37
閱讀 559·2019-08-29 17:24