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