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