摘要:迭代器是一種特殊對象,每一個迭代器對象都有一個,該方法返回一個對象,包括和屬性。默認(rèn)情況下定義的對象是不可迭代的,但是可以通過創(chuàng)建迭代器。在迭代器中拋出錯誤不再執(zhí)行生成器返回語句生成器中添加表示退出操作。迭代器是一個對象。
迭代器(Iterator) ES5實現(xiàn)迭代器
迭代器是什么?遇到這種新的概念,莫慌張。
迭代器是一種特殊對象,每一個迭代器對象都有一個next(),該方法返回一個對象,包括value和done屬性。
ES5實現(xiàn)迭代器的代碼如下:
//實現(xiàn)一個返回迭代器對象的函數(shù),注意該函數(shù)不是迭代器,返回結(jié)果才叫做迭代器。 function createIterator(items) { var i = 0; return { next() { var done = (i >= items.length); // 判斷i是否小于遍歷的對象長度。 var value = !done ? items[i++] : undefined; //如果done為false,設(shè)置value為當(dāng)前遍歷的值。 return { done, value } } } } const a = createIterator([1, 2, 3]); //該方法返回的最終是一個對象,包含value、done屬性。 console.log(a.next()); //{value: 1, done: false} console.log(a.next()); //{value: 2, done: false} console.log(a.next()); //{value: 3, done: false} console.log(a.next()); //{value: undefined, done: true}生成器(Generator)
生成器是函數(shù):用來返回迭代器。
這個概念有2個關(guān)鍵點,一個是函數(shù)、一個是返回迭代器。這個函數(shù)不是上面ES5中創(chuàng)建迭代器的函數(shù),而是ES6中特有的,一個帶有*(星號)的函數(shù),同時你也需要使用到y(tǒng)ield。
//生成器函數(shù),ES6內(nèi)部實現(xiàn)了迭代器功能,你要做的只是使用yield來迭代輸出。 function *createIterator() { yield 1; yield 2; yield 3; } const a = createIterator(); console.log(a.next()); //{value: 1, done: false} console.log(a.next()); //{value: 2, done: false} console.log(a.next()); //{value: 3, done: false} console.log(a.next()); //{value: undefined, done: true}
生成器的yield關(guān)鍵字有個神奇的功能,就是當(dāng)你執(zhí)行一次next(),那么只會執(zhí)行一個yield后面的內(nèi)容,然后語句終止運行。
在for循環(huán)中使用迭代器即使你是在for循環(huán)中使用yield關(guān)鍵字,也會暫停循環(huán)。
function *createIterator(items) { for(let i = 0; i < items.length; i++) { yield items[i] } } const a = createIterator([1, 2, 3]); console.log(a.next()); //{value: 1, done: false}yield使用限制
yield只可以在生成器函數(shù)內(nèi)部使用,如果在非生成器函數(shù)內(nèi)部使用,則會報錯。
function *createIterator(items) { //你應(yīng)該在這里使用yield items.map((value, key) => { yield value //語法錯誤,在map的回調(diào)函數(shù)里面使用了yield }) } const a = createIterator([1, 2, 3]); console.log(a.next()); //無輸出生成器函數(shù)表達(dá)式
函數(shù)表達(dá)式很簡單,就是下面這種寫法,也叫匿名函數(shù),不用糾結(jié)。
const createIterator = function *() { yield 1; yield 2; } const a = createIterator(); console.log(a.next());在對象中添加生成器函數(shù)
一個對象長這樣:
const obj = {}
我們可以在obj中添加一個生成器,也就是添加一個帶星號的方法:
const obj = { a: 1, *createIterator() { yield this.a } } const a = obj.createIterator(); console.log(a.next()); //{value: 1, done: false}可迭代對象和for of循環(huán)
再次默讀一遍,迭代器是對象,生成器是返回迭代器的函數(shù)。
凡是通過生成器生成的迭代器,都是可以迭代的對象(可迭代對象具有Symbol.iterator屬性),也就是可以通過for of將value遍歷出來。
function *createIterator() { yield 1; yield 2; yield 3; } const a = createIterator(); for(let value of a) { console.log(value) } // 1 2 3
上面的例子告訴我們生成器函數(shù)返回的迭代器是一個可以迭代的對象。其實我們這里要研究的是Symbol.iterator的用法。
function *createIterator() { yield 1; yield 2; yield 3; } const a = createIterator(); //a是一個迭代器 const s = a[Symbol.iterator]();//使用Symbol.iterator訪問迭代器 console.log(s.next()) //{value: 1, done: false}
Symbol.iterator還可以用來檢測一個對象是否可迭代:
typeof obj[Symbol.iterator] === "function"創(chuàng)建可迭代對象
在ES6中,數(shù)組、Set、Map、字符串都是可迭代對象。
默認(rèn)情況下定義的對象(object)是不可迭代的,但是可以通過Symbol.iterator創(chuàng)建迭代器。
const obj = { items: [] } obj.items.push(1);//這樣子雖然向數(shù)組添加了新元素,但是obj不可迭代 for (let x of obj) { console.log(x) // _iterator[Symbol.iterator] is not a function } //接下來給obj添加一個生成器,使obj成為一個可以迭代的對象。 const obj = { items: [], *[Symbol.iterator]() { for (let item of this.items) { yield item; } } } obj.items.push(1) //現(xiàn)在可以通過for of迭代obj了。 for (let x of obj) { console.log(x) }內(nèi)建迭代器
上面提到了,數(shù)組、Set、Map都是可迭代對象,即它們內(nèi)部實現(xiàn)了迭代器,并且提供了3種迭代器函數(shù)調(diào)用。
1、entries() 返回迭代器:返回鍵值對
//數(shù)組 const arr = ["a", "b", "c"]; for(let v of arr.entries()) { console.log(v) } // [0, "a"] [1, "b"] [2, "c"] //Set const arr = new Set(["a", "b", "c"]); for(let v of arr.entries()) { console.log(v) } // ["a", "a"] ["b", "b"] ["c", "c"] //Map const arr = new Map(); arr.set("a", "a"); arr.set("b", "b"); for(let v of arr.entries()) { console.log(v) } // ["a", "a"] ["b", "b"]
2、values() 返回迭代器:返回鍵值對的value
//數(shù)組 const arr = ["a", "b", "c"]; for(let v of arr.values()) { console.log(v) } //"a" "b" "c" //Set const arr = new Set(["a", "b", "c"]); for(let v of arr.values()) { console.log(v) } // "a" "b" "c" //Map const arr = new Map(); arr.set("a", "a"); arr.set("b", "b"); for(let v of arr.values()) { console.log(v) } // "a" "b"
3、keys() 返回迭代器:返回鍵值對的key
//數(shù)組 const arr = ["a", "b", "c"]; for(let v of arr.keys()) { console.log(v) } // 0 1 2 //Set const arr = new Set(["a", "b", "c"]); for(let v of arr.keys()) { console.log(v) } // "a" "b" "c" //Map const arr = new Map(); arr.set("a", "a"); arr.set("b", "b"); for(let v of arr.keys()) { console.log(v) } // "a" "b"
雖然上面列舉了3種內(nèi)建的迭代器方法,但是不同集合的類型還有自己默認(rèn)的迭代器,在for of中,數(shù)組和Set的默認(rèn)迭代器是values(),Map的默認(rèn)迭代器是entries()。
for of循環(huán)解構(gòu)對象本身不支持迭代,但是我們可以自己添加一個生成器,返回一個key,value的迭代器,然后使用for of循環(huán)解構(gòu)key和value。
const obj = { a: 1, b: 2, *[Symbol.iterator]() { for(let i in obj) { yield [i, obj[i]] } } } for(let [key, value] of obj) { console.log(key, value) } // "a" 1, "b" 2字符串迭代器
const str = "abc"; for(let v of str) { console.log(v) } // "a" "b" "c"NodeList迭代器
迭代器真是無處不在啊,dom節(jié)點的迭代器你應(yīng)該已經(jīng)用過了。
const divs = document.getElementByTagName("div"); for(let d of divs) { console.log(d) }展開運算符和迭代器
const a = [1, 2, 3]; const b = [4, 5, 6]; const c = [...a, ...b] console.log(c) // [1, 2, 3, 4, 5, 6]高級迭代器功能
你說什么?上面講了一堆廢話都是基礎(chǔ)功能?還有高級功能沒講?
高級功能不復(fù)雜,就是傳參、拋出異常、生成器返回語句、委托生成器。
1、傳參
生成器里面有2個yield,當(dāng)執(zhí)行第一個next()的時候,返回value為1,然后給第二個next()傳入?yún)?shù)10,傳遞的參數(shù)會替代掉上一個next()的yield返回值。在下面的例子中就是first。
function *createIterator() { let first = yield 1; yield first + 2; } let i = createIterator(); console.log(i.next()); // {value: 1, done: false} console.log(i.next(10)); // {value: 12, done: false}
2、在迭代器中拋出錯誤
function *createIterator() { let first = yield 1; yield first + 2; } let i = createIterator(); console.log(i.next()); // {value: 1, done: false} console.log(i.throw(new Error("error"))); // error console.log(i.next()); //不再執(zhí)行
3、生成器返回語句
生成器中添加return表示退出操作。
function *createIterator() {
let first = yield 1;
return;
yield first + 2;
}
let i = createIterator();
console.log(i.next()); // {value: 1, done: false}
console.log(i.next()); // {value: undefined, done: true}
4、委托生成器
生成器嵌套生成器
function *aIterator() { yield 1; } function *bIterator() { yield 2; } function *cIterator() { yield *aIterator() yield *bIterator() } let i = cIterator(); console.log(i.next()); // {value: 1, done: false} console.log(i.next()); // {value: 2, done: false}異步任務(wù)執(zhí)行器
ES6之前,我們使用異步的操作方式是調(diào)用函數(shù)并執(zhí)行回調(diào)函數(shù)。
書上舉的例子挺好的,在nodejs中,有一個讀取文件的操作,使用的就是回調(diào)函數(shù)的方式。
var fs = require("fs"); fs.readFile("xx.json", function(err, contents) { //在回調(diào)函數(shù)中做一些事情 })
那么任務(wù)執(zhí)行器是什么呢?
任務(wù)執(zhí)行器是一個函數(shù),用來循環(huán)執(zhí)行生成器,因為我們知道生成器需要執(zhí)行N次next()方法,才能運行完,所以我們需要一個自動任務(wù)執(zhí)行器幫我們做這些事情,這就是任務(wù)執(zhí)行器的作用。
下面我們編寫一個異步任務(wù)執(zhí)行器。
//taskDef是一個生成器函數(shù),run是異步任務(wù)執(zhí)行器 function run(taskDef) { let task = taskDef(); //調(diào)用生成器 let result = task.next(); //執(zhí)行生成器的第一個next(),返回result function step() { if(!result.done) { //如果done為false,則繼續(xù)執(zhí)行next(),并且循環(huán)step,直到done為true退出。 result = task.next(result.value); step(); } } step(); //開始執(zhí)行step() }
測試一下我們編寫的run方法,我們不再需要console.log N個next了,因為run執(zhí)行器已經(jīng)幫我們做了循環(huán)執(zhí)行操作:
run(function *() { let value = yield 1; value = yield value + 20; console.log(value) // 21 })總結(jié)
本章講了3個概念,迭代器、生成器、任務(wù)執(zhí)行器。
迭代器是一個對象。
生成器是一個函數(shù),它最終返回迭代器。
任務(wù)執(zhí)行器一個函數(shù)(或者也叫生成器的回調(diào)函數(shù)),幫我們自動執(zhí)行生成器的內(nèi)部運算,最終返回迭代器。
不知道看到這里,你明白3者的區(qū)別和用法沒?
=> 返回文章列表
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/84325.html
摘要:可迭代對象就具有屬性,它是一種與迭代器密切相關(guān)的對象。它通過指定的函數(shù)可以返回一個作用于附屬對象的迭代器。迭代器特點每次調(diào)用方法時,返回一個數(shù)組,數(shù)組中兩個元素,分別表示鍵和值。示例之輸出輸出輸出之迭代器特點返回集合中存在的每一個鍵。 Iterator由來 不推薦Iterator方法。 Iterator 函數(shù)是一個 SpiderMonkey 專有特性,并且會在某一時刻被刪除。有一點,需...
摘要:迭代器和生成器將迭代的概念直接帶入核心語言,并提供一種機(jī)制來自定義循環(huán)的行為。本文主要會介紹中新增的迭代器和生成器。屬性本身是函數(shù),是當(dāng)前數(shù)據(jù)結(jié)構(gòu)默認(rèn)的迭代器生成函數(shù)。 本文是 重溫基礎(chǔ) 系列文章的第十三篇。今日感受:每次自我年終總結(jié),都會有各種情緒和收獲。 系列目錄: 【復(fù)習(xí)資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基礎(chǔ)】1.語法和數(shù)據(jù)類型 【重溫基礎(chǔ)】2.流...
摘要:最近買了深入理解的書籍來看,為什么學(xué)習(xí)這么久還要買這本書呢主要是看到核心團(tuán)隊成員及的創(chuàng)造者為本書做了序,作為一個粉絲,還是挺看好這本書能給我?guī)硪粋€新的升華,而且本書的作者也非常厲害。 使用ES6開發(fā)已經(jīng)有1年多了,以前看的是阮一峰老師的ES6教程,也看過MDN文檔的ES6語法介紹。 最近買了《深入理解ES6》的書籍來看,為什么學(xué)習(xí)ES6這么久還要買這本書呢?主要是看到Daniel A...
閱讀 1360·2023-04-25 23:42
閱讀 2855·2021-11-19 09:40
閱讀 3534·2021-10-19 11:44
閱讀 3573·2021-10-14 09:42
閱讀 1876·2021-10-13 09:39
閱讀 3843·2021-09-22 15:43
閱讀 679·2019-08-30 15:54
閱讀 1461·2019-08-26 13:32