摘要:可迭代對象就具有屬性,它是一種與迭代器密切相關(guān)的對象。它通過指定的函數(shù)可以返回一個作用于附屬對象的迭代器。迭代器特點每次調(diào)用方法時,返回一個數(shù)組,數(shù)組中兩個元素,分別表示鍵和值。示例之輸出輸出輸出之迭代器特點返回集合中存在的每一個鍵。
Iterator由來
不推薦Iterator方法。 Iterator 函數(shù)是一個 SpiderMonkey 專有特性,并且會在某一時刻被刪除。
有一點,需要清楚的,就是“迭代協(xié)議”。迭代協(xié)議MDN說明
// 簡單示例,摘自“深入理解ES6” function createIterator(items) { let i = 0; return { next: function() { let done = (i >= items.length); let value = !done ? items[i++] : undefined; return { done, value } } } } let iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // { done: false, value: 1 } console.log(iterator.next()); // { done: false, value: 2 } console.log(iterator.next()); // { done: false, value: 3 } console.log(iterator.next()); // { done: true, value: undefined } // 之后所有的調(diào)用都會返回相同的內(nèi)容 console.log(iterator.next()); // { done: true, value: undefined }Generator定義
生成器是一種返回迭代器的函數(shù),通過function關(guān)鍵字后的星號(*)來表示,函數(shù)中會用到新的關(guān)鍵字yield。星號可以緊挨著function關(guān)鍵字,也可以在中間加一個空格。
function *createIterator() { yield 1; yield 2; yield 3; } let iterator = createIterator(); console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: 3, done: false } console.log(iterator.next()); // { value: undefined, done: true } // 換個方法 function *createIterator(items) { for (let i = 0; i < items.length; i++) { yield items[i]; } } let iterator = createIterator([1, 2, 3]); // iterator 與前面代碼創(chuàng)建的 iterator 功能一樣,可以試一下。
yield的使用限制
yield關(guān)鍵字只能在生成器內(nèi)部使用,在其他地方使用會導(dǎo)致程序拋出語法錯誤,即便在生成器的函數(shù)里使用也如此。
function *createIterator(items) { items.forEach(function(item) { yield item + 1; }); } // 會報語法錯誤 node ./iterator.js
從字面理解,yield關(guān)鍵字確定在createIterator()函數(shù)內(nèi)部,但是它與return關(guān)鍵字一樣,二者都不能穿透函數(shù)邊界。嵌套函數(shù)中的return語句不能用作函數(shù)的返回語句,而此處嵌套函數(shù)中的yield語句會導(dǎo)致程序拋出語法錯誤。
生成器函數(shù)表達式&對象方法
通過上面的方法,關(guān)于函數(shù)表達式和對象方法,直接上代碼吧,更明白。
// 函數(shù)表達式 let createIterator = function *(items) { for (let i =0; i < items.length; i++) { yield items[i]; } } let iterator = createIterator([1, 2, 3]); // 對象方法 let o = { createIterator: function *(items) { yield items[i]; } } let iterator = o.createIterator([1, 2, 3]);可迭代對象 & for-of 循環(huán)
看過Symbol文章的小伙伴應(yīng)該都知道,Symbol.iterator就是 well-known Symbol之一??傻鷮ο缶途哂蠸ymbol.iterator屬性,它是一種與迭代器密切相關(guān)的對象。它通過指定的函數(shù)可以返回一個作用于附屬對象的迭代器。在ES6中,所有的集合對象(Array, Set, Map)和字符串都是可迭代對象,這些對象中都有默認的迭代器。當然,ES中也添加了for-of循環(huán)這些可迭代對象。
迭代器
for-of循環(huán)
這是解決循環(huán)內(nèi)部索引跟蹤問題的關(guān)鍵工具。
for-of循環(huán)每執(zhí)行一次都會調(diào)用可迭代對象中的next()方法,并將迭代器返回的結(jié)果對象的value屬性存儲在一個變量中,循環(huán)將持續(xù)執(zhí)行這一過程直到返回對象的done屬性為true。
let values = [1, 2, 3]; for (let num of values) { console.log(num); } // 輸出: // 1 // 2 // 3
示例說明:
for-of循環(huán)的代碼通過調(diào)用values數(shù)組的Symbol.iterator方法來獲取迭代器,這一過程是在Javascript引擎背后完成的。隨后迭代器的next()的方法被多次調(diào)用,從其返回對象的value屬性讀取值并存儲在變量num中,直到對象的done為true時循環(huán)退出,所以num不會賦值為undefined
從上面的例子可以看出,可迭代對象都有一個默認迭代器。這個迭代器可通過Symbol.iterator來訪問。
let values = [1, 2, 3]; let iterator = values[Symbol.iterator](); console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: 3, done: false } console.log(iterator.next()); // { value: undefined, done: true }
由此,我們可以判斷對象是否可迭代,是不是有更好的方法?
function isIterator(object) { return typeof object[Symbol.iterator] === "function"; } console.log(isIterator([1, 2, 3])); // true console.log(isIterator("Hello")); // true console.log(isIterator(new Map())); // true console.log(isIterator(new Set())); // true console.log(isIterator(new WeakMap())); // false console.log(isIterator(new WeakSet())); // false創(chuàng)建可迭代對象
默認情況下,開發(fā)者定義的對象都是不可迭代的對象,但如果給Symbol.iterator屬性添加一個生成器,則可以將其變?yōu)榭傻鷮ο蟆?/p>
let collection = { items: [], *[Symbol.iterator]() { for (let item of this.items) { yield item; } } }; collection.items.push(1); collection.items.push(2); collection.items.push(3); for (let item of collection){ console.log(item); } // 輸出: // 1 // 2 // 3內(nèi)建迭代器
到這里,應(yīng)該明白,ES6中已經(jīng)默認為很多內(nèi)建類型提供了內(nèi)建迭代器,只有這些內(nèi)容無法實現(xiàn)目標時,才需要自己創(chuàng)建。ES6中有三種集合對象: 數(shù)組、Map與Set集合,他們內(nèi)建了以下三種迭代器:
entries() 返回值為多個鍵值對迭代器
values() 返回值為集合的迭代器
keys() 返回集合中所有鍵名迭代器
MDN 關(guān)于內(nèi)建迭代器 說的比較簡單,我就按“深入理解ES6”來詳細說一下吧。
entries() 迭代器特點:每次調(diào)用next()方法時,entries()返回一個數(shù)組,數(shù)組中兩個元素,分別表示鍵和值。
- Array 第一個元素為數(shù)字類型的索引,第二個元素為值 - Set 第一個元素與第二個元素都是值,因為Set中值也作為鍵來使用 - Map 第一個元素為鍵,第二個元素為值 示例:
let colors = [ "red", "green", "blue"]; let tracking = new Set([ 123, 456, 890]); let data = new Map(); data.set("title", "ES6之Iterator&Generator"); data.set("formate", "net share"); for (let entry of colors.entries()) { console.log(entry); } // 輸出: // [ 0, "red" ] // [ 1, "green" ] // [ 2, "blue" ] for (let entry of tracking.entries()) { console.log(entry); } // 輸出: // [ 123, 123 ] // [ 456, 456 ] // [ 890, 890 ] for (let entry of data.entries()) { console.log(entry); } // 輸出: // [ "title", "ES6之Iterator&Generator" ] // [ "formate", "net share" ]values() 迭代器
特點: 返回集合中所存的所有值。
示例:
let colors = [ "red", "green", "blue"]; let tracking = new Set([ 123, 456, 890]); let data = new Map(); data.set("title", "ES6之Iterator&Generator"); data.set("formate", "net share"); for (let entry of colors.values()) { console.log(entry); } // 輸出: // red // green // blue for (let entry of tracking.values()) { console.log(entry); } // 輸出: // 123 // 456 // 890 for (let entry of data.values()) { console.log(entry); } // 輸出: // ES6之Iterator&Generator // net sharekeys() 迭代器
特點:返回集合中存在的每一個鍵。
示例:
let colors = [ "red", "green", "blue"]; let tracking = new Set([ 123, 456, 890]); let data = new Map(); data.set("title", "ES6之Iterator&Generator"); data.set("formate", "net share"); for (let entry of colors.keys()) { console.log(entry); } // 輸出: // 0 // 1 // 2 for (let entry of tracking.keys()) { console.log(entry); } // 輸出: // 123 // 456 // 890 for (let entry of data.keys()) { console.log(entry); } // 輸出: // title // formate
不同集合,會使用不同的默認迭代器,Array和Set使用的是values()迭代器,而Map則使用的是entries()迭代器。
string 迭代器特點: 與Array類似。string也可以通過下標訪問字符內(nèi)容。由于下標操作是編碼單元而非字符,所以無法訪問雙字節(jié)符。在Unicode支持不好的版本(node或瀏覽器引擎),會出現(xiàn)錯誤。如果使用for-of,則不會出現(xiàn)這種問題,因為其操作的是字符而非編碼單元。
思考一個問題:展開運算符與非數(shù)組可迭代對象
如果對可迭代對象使用展開運算,會調(diào)用默認迭代器嗎?返回又是什么呢?
非數(shù)組可迭代對象呢?
示例:
let o = { a: "aaaa", b: 123, c: "ffffd", *[Symbol.iterator]() { yield this.a; yield this.b; yield this.c; } } let ar = [...o]; console.log(ar); for(let item of ar) { console.log(item); } // 輸出 // [ "aaaa", 123, "ffffd" ] // aaaa // 123 // ffffd迭代器高級功能 給迭代器傳參
這次先做示例,再做說明。
示例1:
function *createIterator() { let first = yield 1; let second = yield first + 2; yield second + 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next(4)); console.log(iterator.next(6)); console.log(iterator.next()); // 輸出: // { value: 1, done: false } // { value: 6, done: false } // { value: 9, done: false } // { value: undefined, done: true }
示例2:
function *createIterator() { let first = yield 1; let second = yield first + 2; yield first + 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next(4)); console.log(iterator.next(6)); console.log(iterator.next()); // 輸出: // { value: 1, done: false } // { value: 6, done: false } // { value: 7, done: false } // { value: undefined, done: true }
示例3:
function *createIterator() { let first = yield 1; let second = yield first + 2; yield second + 3; } let iterator = createIterator(); console.log(iterator.next(5)); console.log(iterator.next(4)); console.log(iterator.next(6)); console.log(iterator.next()); // 輸出: // { value: 1, done: false } // { value: 6, done: false } // { value: 9, done: false } // { value: undefined, done: true }
示例4
function *createIterator() { let first = 1; yield first; let second = yield first + 2; yield second + 3; } let iterator = createIterator(); console.log(iterator.next(5)); console.log(iterator.next(4)); console.log(iterator.next(6)); console.log(iterator.next()); // 示例: // { value: 1, done: false } // { value: 3, done: false } // { value: 9, done: false } // { value: undefined, done: true }
示例5:
function *createIterator() { let first = yield 1; let second = yield first + 2; yield second + 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); // 輸出: // { value: 1, done: false } // { value: NaN, done: false } // { value: NaN, done: false } // { value: undefined, done: true }
示例5的輸出結(jié)果,是不是很意外?yield返回變量與正常變量賦值有何不同?
分析:
傳遞參數(shù)會替代上一次yield的返回值
第一個next()執(zhí)行,傳參無效。因為第一次調(diào)用yield時,之前沒有任何yield語句執(zhí)行
非yield返回值 ,不受next()參數(shù)影響
在一個含參的yield語句中,表達式右側(cè)等價于第一次調(diào)用next()方法后的下一個返回值。表達式左側(cè)等價于第二次調(diào)用next()方法后,在函數(shù)繼續(xù)執(zhí)行前得到的返回值。
拋出錯誤這個也先示例,后說明。
示例1:
function *createIterator() { let first = yield 1; let second = yield first + 2; yield second + 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next(4)); console.log(iterator.throw(new Error("Boom"))); console.log(iterator.next(5)); // 輸出: // { value: 1, done: false } // { value: 6, done: false } // xx/iterator.js:146 // let second = yield first + 2; // ^ // // Error: Boom
分析:前兩個表達式正常求值,在繼續(xù)執(zhí)行l(wèi)et second 求值前,錯誤就會被拋出,并阻止了代碼繼續(xù)執(zhí)行。
這個過程,與直接拋出異常很相似,只是拋出的時機不同。
示例2
function *createIterator() { let first = yield 1; let second ; try { second = yield first + 2; } catch (ex){ second = 6; } yield second + 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next(4)); console.log(iterator.throw(new Error("Boom"))); console.log(iterator.next(5)); // 輸出: // { value: 1, done: false } // { value: 6, done: false } // { value: 9, done: false } // { value: undefined, done: true }
分析:
用try...catch語句來捕獲異常,包裹著第二名語句。盡管這條語句本身沒有錯誤,但在給second賦值前,還是會拋出錯誤,catch代碼塊捕捉到這個錯誤后,并把second = 6. 下一條yield語句繼續(xù)執(zhí)行后,返回9.
基本上兩個示例可以概括。
示例1
function *createIterator() { yield 1; return; yield 2; yield 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); // 輸出: // { value: 1, done: false } // { value: undefined, done: true } // { value: undefined, done: true }
示例2
function *createIterator() { yield 1; return 33; yield 2; yield 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); // 輸出: // { value: 1, done: false } // { value: 33, done: true } // { value: undefined, done: true }委托生成
先看三個示例吧,這個只是語法規(guī)范。
示例1
function *createNumIterator() { yield 1; yield 2; } function *createColorIterator() { yield "red"; yield "green"; } function *createCombinedIterator() { yield *createNumIterator(); yield *createColorIterator(); yield true; } var iterator = createCombinedIterator(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); // 輸出: // { value: 1, done: false } // { value: 2, done: false } // { value: "red", done: false } // { value: "green", done: false } // { value: true, done: false } // { value: undefined, done: true }
分析:語法規(guī)范,多個迭代器合并,可以創(chuàng)建一個生成器,再給yield語句添加一個號,就可以將生成數(shù)據(jù)的過程委托給其他迭代器。當定義這些生成器時,只需要將號放置在關(guān)鍵字yield和生成器的函數(shù)名之間即可。
示例2
function *createNumIterator() { yield 1; yield 2; return 3; } function *createRepeatIterator(count) { for (let i = 0; i < count; i++){ yield "repeat"; } } function *createCombinedIterator() { let result = yield *createNumIterator(); yield *createRepeatIterator(result); } var iterator = createCombinedIterator(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); // 輸出 : // { value: 1, done: false } // { value: 2, done: false } // { value: "repeat", done: false } // { value: "repeat", done: false } // { value: "repeat", done: false } // { value: undefined, done: true }
分析:執(zhí)行過程,先被委托給了createNumIterator(),返回值會被賦給變量result,執(zhí)行到return 3時,返回精數(shù)值3。這個值隨后被傳入createRepeatIterator()方法。
但是,無論通過何種方式調(diào)用next()方法,數(shù)值3永遠不會被返回,它只存在于createCombinedIterator()的內(nèi)部。如果要輸出3,參看示例3。
示例3
function *createNumIterator() { yield 1; yield 2; return 3; } function *createRepeatIterator(count) { for (let i = 0; i < count; i++){ yield "repeat"; } } function *createCombinedIterator() { let result = yield *createNumIterator(); yield result; yield *createRepeatIterator(result); } var iterator = createCombinedIterator(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); // 輸出: // { value: 1, done: false } // { value: 2, done: false } // { value: 3, done: false } // { value: "repeat", done: false } // { value: "repeat", done: false } // { value: "repeat", done: false } // { value: undefined, done: true }
期待的3,出來了。
異步任務(wù)執(zhí)行生成器支持在函數(shù)中暫停代碼執(zhí)行,可以挖掘異步處理的更多方法。
簡單任務(wù)執(zhí)行器示例:
function run(taskDef) { // 創(chuàng)建迭代器 let task = taskDef(); // 開始執(zhí)行任務(wù) let result = task.next(); function step() { if (!result.done) { result = task.next(); step(); } } step(); } run(function *() { console.log(1); yield; console.log(2); yield; console.log(3); yield; });
分析:
函數(shù)run()接受一個生成器作為參數(shù),這個函數(shù)定義了后續(xù)要執(zhí)行的任務(wù),生成一個迭代器并將它存儲在變量task中。
首次調(diào)用next(),返回的結(jié)果被存儲起來,稍后繼續(xù)使用。
step()函數(shù)檢查result.done的值,false時,繼續(xù)執(zhí)行next()方法,并執(zhí)行step()操作。
每次執(zhí)行next()返回值會覆蓋變量result原來的值
傳參示例:
function run(taskDef) { // 創(chuàng)建迭代器 let task = taskDef(); // 開始執(zhí)行任務(wù) let result = task.next(); function step() { if (!result.done) { result = task.next(result.value); step(); } } step(); } run(function *() { let value = yield 1; console.log(value); value = yield value + 3; console.log(value); });
注意yield表達式求值。
異步示例:
// redux-saga 經(jīng)典應(yīng)用
redux-saga
Iterator&Generator 之 MDN 說明
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/105636.html
摘要:第二次同理,遇到了第二個函數(shù)會停下來,輸出的遍歷器對象值為,的值依然是。比如返回的遍歷器對象,都會有一個方法,這個方法掛在原型上。這三個函數(shù)共同的作用是讓函數(shù)恢復(fù)執(zhí)行。 Generator的語法 generator的英文意思是生成器 簡介 關(guān)于Generator函數(shù),我們可以理解成是一個狀態(tài)機,里面封裝了多種不同的狀態(tài)。 function* gener(){ yield hel...
摘要:更好的語義和分別表示異步和等待,比起和更容易理解。前邊聲明關(guān)鍵字,表示內(nèi)部有內(nèi)部操作,調(diào)用函數(shù)會返回一個對象。等價于其中函數(shù)就是自動執(zhí)行器。 async函數(shù) 定義 async函數(shù)其實就是之前說過的Generator的語法糖,用于實現(xiàn)異步操作。它是ES2017的新標準。 讀取兩個文件: const fs = require(fs) const readFile = function(f...
摘要:舉個例子遍歷器生成函數(shù),作用就是返回一個遍歷器對象,方法返回一個對象,表示當前數(shù)據(jù)成員的信息。該對象本身也具有屬性,執(zhí)行后返回自身。 Iterator的作用 一是為各種數(shù)據(jù)結(jié)構(gòu),提供一個統(tǒng)一的、簡便的訪問接口;(統(tǒng)一)二是使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列;(按序)三是ES6創(chuàng)造了一種新的遍歷命令for...of循環(huán),Iterator接口主要供for...of消費。舉個例子:遍歷器生...
摘要:舉個例子遍歷器生成函數(shù),作用就是返回一個遍歷器對象,方法返回一個對象,表示當前數(shù)據(jù)成員的信息。該對象本身也具有屬性,執(zhí)行后返回自身。 Iterator的作用 一是為各種數(shù)據(jù)結(jié)構(gòu),提供一個統(tǒng)一的、簡便的訪問接口;(統(tǒng)一)二是使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列;(按序)三是ES6創(chuàng)造了一種新的遍歷命令for...of循環(huán),Iterator接口主要供for...of消費。舉個例子:遍歷器生...
閱讀 1627·2021-11-22 14:45
閱讀 1077·2021-11-17 09:33
閱讀 3329·2021-09-02 09:48
閱讀 977·2019-08-30 15:54
閱讀 2775·2019-08-30 15:53
閱讀 2562·2019-08-30 12:54
閱讀 2251·2019-08-29 12:37
閱讀 2430·2019-08-26 13:58