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