摘要:舉個例子遍歷器生成函數,作用就是返回一個遍歷器對象,方法返回一個對象,表示當前數據成員的信息。該對象本身也具有屬性,執行后返回自身。
Iterator的作用
一是為各種數據結構,提供一個統一的、簡便的訪問接口;(統一)
二是使得數據結構的成員能夠按某種次序排列;(按序)
三是ES6創造了一種新的遍歷命令for...of循環,Iterator接口主要供for...of消費。
舉個例子:遍歷器生成函數,作用就是返回一個遍歷器對象,next方法返回一個對象,表示當前數據成員的信息。這個對象具有value和done兩個屬性,value屬性返回當前位置的成員,done屬性是一個布爾值,表示遍歷是否結束
function makeIterator(array) { var nextIndex = 0; return { next: function() { return nextIndex < array.length ? {value: array[nextIndex++], done: false} : {value: undefined, done: true}; } }; }
對于遍歷器對象來說,done: false和value: undefined屬性都是可以省略的,所以上述代碼可以簡寫為:
function makeIterator(array) { var nextIndex = 0; return { next: function() { return nextIndex < array.length ? {value: array[nextIndex++]} : {done: true}; } }; }
Iterator 只是把接口規格加到數據結構之上,所以,遍歷器與它所遍歷的那個數據結構,實際上是分開的,完全可以寫出沒有對應數據結構的遍歷器對象,或者說用遍歷器對象模擬出數據結構
Iterator接口默認的 Iterator 接口部署在數據結構的Symbol.iterator屬性,Symbol.iterator屬性本身是一個函數,就是當前數據結構默認的遍歷器生成函數。執行這個函數,就會返回一個遍歷器
原生具備 Iterator 接口的數據結構如下:
1)Array
2)Map
3)Set
4)String
5)TypedArray
6)函數的 arguments 對象
對于部署了Iterator接口的數據結構除了可以使用for of循環之外,可以用while判斷對象的done屬性進行循環遍歷
怎么樣使用原生的遍歷器呢?
let arr = ["a", "b", "c"]; let iter = arr[Symbol.iterator](); iter.next() //第二個例子: var someString = "hi"; typeof someString[Symbol.iterator] // "function" var iterator = someString[Symbol.iterator](); iterator.next() // { value: "h", done: false }
對于類似數組的對象應該怎樣調用數組的Symbol.iterator方法?
let iterable = { 0: "a", 1: "b", 2: "c", length: 3, [Symbol.iterator]: Array.prototype[Symbol.iterator]//這句話是重點 }; for (let item of iterable) { console.log(item); // "a", "b", "c" }使用Iterator的場景
解構賦值
let set = new Set().add("a").add("b").add("c"); let [first, ...rest] = set; // first="a"; rest=["b","c"]; //對數組和 Set 結構進行解構賦值時,會默認調用Symbol.iterator方法
擴展運算符
var str = "hello"; [...str] // ["h","e","l","l","o"] //擴展運算符(...)也會調用默認的 Iterator 接口
yield*
let generator = function* () { yield 1; yield* [2,3,4]; yield 5; }; var iterator = generator(); iterator.next()遍歷器的return和throw方法
遍歷器對象生成函數,next方法是必須部署的,return方法和throw方法是否部署是可選的。return方法必須返回一個對象
function readLinesSync(file) { return { next() { if (file.isAtEndOfFile()) { file.close(); return { done: true }; } }, return() { file.close(); return { done: true }; }, }; } for (let line of readLinesSync(fileName)) { console.log(line); break;//我們讓文件的遍歷提前返回,這樣就會觸發執行return方法 }for of 循環優點
forEach沒辦法跳出循環,也就是說在forEach當中break命令或return命令都不能奏效
for...in循環不僅遍歷數字鍵名,還會遍歷手動添加的其他鍵,甚至包括原型鏈上的鍵,而且for in 是沒有順序的(也就是說for...in循環主要是為遍歷對象而設計的,不適用于遍歷數組)
小訣竅:
1、并不是所有類似數組的對象都具有 Iterator 接口,一個簡便的解決方法,就是使用Array.from方法將其轉為數組
let arrayLike = { length: 2, 0: "a", 1: "b" }; // 報錯 for (let x of arrayLike) { console.log(x); } // 正確 for (let x of Array.from(arrayLike)) { console.log(x); }
2、因為普通對象并沒有部署Iterator接口,所以是無法使用for of循環的,用以下兩種方案解決:
for (var key of Object.keys(someObject)) { console.log(key + ": " + someObject[key]); } //第二種方式: function* entries(obj) { for (let key of Object.keys(obj)) { yield [key, obj[key]]; } } for (let [key, value] of entries(obj)) { console.log(key, "->", value); } //第三種方式: function* objectEntries(obj) { let propKeys = Reflect.ownKeys(obj); for (let propKey of propKeys) { yield [propKey, obj[propKey]]; } } for (let [key, value] of objectEntries(obj)) { console.log(`${key}: ${value}`); } //或者也可以這樣 let jane = { first: "Jane", last: "Doe" }; jane[Symbol.iterator] = objectEntries;//這句話是重點將 Generator 函數加到對象的Symbol.iterator屬性上面 for (let [key, value] of jane) { console.log(`${key}: ${value}`); }Generator簡介
在我看來Generator就是為了異步編程提供一種解決方案;
Generator 函數是一個普通函數,事實上它是遍歷器生成函數,但有幾個特性:
1、function關鍵字與函數名之間有一個星號(星號緊跟在function關鍵字后面);
2、函數體內部使用yield表達式,定義不同的內部狀態(yield在英語里的意思就是“產出” 1)yield只能在Generator函數中使用;2)yield表達式如果用在另一個表達式之中,必須放在圓括號里面;3)yield表達式用作函數參數或放在賦值表達式的右邊,可以不加括號);
3、可以把Generator函數理解為狀態機,調用 Generator 函數后,該函數并不執行,返回的也不是函數運行結果,而是一個指向內部狀態的指針對象,每次調用next方法,內部指針就從函數頭部或上一次停下來的地方開始執行,直到遇到下一個yield表達式(或return語句)為止;Generator 函數是分段執行的,yield表達式是暫停執行的標記,而next方法可以恢復執行。
var myIterable = {}; myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; [...myIterable]
Generator 函數執行后,返回一個遍歷器對象。該對象本身也具有Symbol.iterator屬性,執行后返回自身。
function* gen(){ // some code } var g = gen(); g[Symbol.iterator]() === gGenerator中next方法的參數
通過next方法的參數,就有辦法在 Generator 函數開始運行之后,繼續向函數體內部注入值
由于next方法的參數表示上一個yield表達式的返回值,所以第一次使用next方法時,不能帶有參數。V8 引擎直接忽略第一次使用next方法時的參數,只有從第二次使用next方法開始,參數才是有效的
舉個簡單的例子吧:
function* dataConsumer() { console.log("Started"); console.log(`1. ${yield}`); console.log(`2. ${yield}`); return "result"; } let genObj = dataConsumer(); genObj.next(); // Started genObj.next("a") // 1. a genObj.next("b") // 2. b
復雜一點的:
function* foo(x) { var y = 2 * (yield (x + 1)); var z = yield (y / 3); return (x + y + z); } var a = foo(5); a.next() // Object{value:6, done:false} a.next() // Object{value:NaN, done:false} a.next() // Object{value:NaN, done:true} var b = foo(5); b.next() // { value:6, done:false } b.next(12) // { value:8, done:false } //解釋一下結果y=2*12 所以返回的是24/3 b.next(13) // { value:42, done:true } //y=24 z=13 所以返回的是5+24+13說說for of與Generator的關系吧
有一點需要注意的是當next方法中返回done為true則終止循環且不包含該返回對象,所以上面代碼的return語句返回的6,不包括在for...of循環之中
function *foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } for (let v of foo()) { console.log(v); }Generator中的throw方法
Generator 函數返回的遍歷器對象,都有一個throw方法,可以在函數體外拋出錯誤,然后在 Generator 函數體內捕獲,throw方法可以接受一個參數,該參數會被catch語句接收
var g = function* () { try { yield; } catch (e) { console.log(e); } }; var i = g(); i.next(); i.throw(new Error("出錯了!")); // Error: 出錯了!(…)
throw方法被捕獲以后,會附帶執行下一條yield表達式。也就是說,會附帶執行一次next方法。
var gen = function* gen(){ try { yield console.log("a"); } catch (e) { // ... } yield console.log("b"); yield console.log("c"); } var g = gen(); g.next() // a g.throw() // b g.next() // c
Generator中的throw有什么優勢呢?多個yield表達式,可以只用一個try...catch代碼塊來捕獲錯誤,大大方便了對錯誤的處理。
一旦 Generator 執行過程中拋出錯誤,且沒有被內部捕獲,就不會再執行下去了
function* g() { yield 1; console.log("throwing an exception"); throw new Error("generator broke!"); yield 2; yield 3; }
如上所示代碼:因為拋出了異常generator broke,所以后面的2 3都不會返回
Generator returnfunction* gen() { yield 1; yield 2; yield 3; } var g = gen(); g.next() // { value: 1, done: false } g.return("foo") // { value: "foo", done: true } g.next() // { value: undefined, done: true }
g調用return方法后,返回值的value屬性就是return方法的參數foo。并且,Generator函數的遍歷就終止了,返回值的done屬性為true,以后再調用next方法,done屬性總是返回true
另外一個特殊情況: Generator 函數內部有try...finally代碼塊,那么return方法會推遲到finally代碼塊執行完再執行。也就是說:調用return方法后,就開始執行finally代碼塊,然后等到finally代碼塊執行完,再執行return方法
function* numbers () { yield 1; try { yield 2; yield 3; } finally { yield 4; yield 5; } yield 6; } var g = numbers(); g.next() // { value: 1, done: false } g.next() // { value: 2, done: false } g.return(7) // { value: 4, done: false } g.next() // { value: 5, done: false } g.next() // { value: 7, done: true }yield*表達式
yield表達式后面跟的是一個遍歷器對象,需要在yield表達式后面加上星號,表明它返回的是一個遍歷器對象。這被稱為yield*表達式
let delegatedIterator = (function* () { yield "Hello!"; yield "Bye!"; }()); let delegatingIterator = (function* () { yield "Greetings!"; yield* delegatedIterator; yield "Ok, bye."; }()); for(let value of delegatingIterator) { console.log(value); } // "Greetings! // "Hello!" // "Bye!" // "Ok, bye."Generator函數中的this
Generator函數不能跟new命令一起用
怎樣讓Generator 函數返回一個正常的對象實例,既可以用next方法,又可以獲得正常的this?
function* F() { this.a = 1; yield this.b = 2; yield this.c = 3; } //這是第一種方式 var obj = {}; var f = F.call(obj);//讓F內部的this對象綁定obj對象 //第二種方式 var f = F.call(F.prototype); f.next(); // Object {value: 2, done: false} f.next(); // Object {value: 3, done: false} f.next(); // Object {value: undefined, done: true} obj.a // 1 obj.b // 2 obj.c // 3
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84035.html
摘要:可迭代對象就具有屬性,它是一種與迭代器密切相關的對象。它通過指定的函數可以返回一個作用于附屬對象的迭代器。迭代器特點每次調用方法時,返回一個數組,數組中兩個元素,分別表示鍵和值。示例之輸出輸出輸出之迭代器特點返回集合中存在的每一個鍵。 Iterator由來 不推薦Iterator方法。 Iterator 函數是一個 SpiderMonkey 專有特性,并且會在某一時刻被刪除。有一點,需...
摘要:舉個例子遍歷器生成函數,作用就是返回一個遍歷器對象,方法返回一個對象,表示當前數據成員的信息。該對象本身也具有屬性,執行后返回自身。 Iterator的作用 一是為各種數據結構,提供一個統一的、簡便的訪問接口;(統一)二是使得數據結構的成員能夠按某種次序排列;(按序)三是ES6創造了一種新的遍歷命令for...of循環,Iterator接口主要供for...of消費。舉個例子:遍歷器生...
摘要:返回的遍歷器對象可以依次遍歷函數內部的每一個狀態。示例內部捕獲外部捕獲內部捕獲外部捕獲上面代碼遍歷器對象連續拋出兩個錯誤,第一個被函數體內的捕獲。上面代碼中,首先執行函數,獲取遍歷器對象,然后使用方法第二行,執行異步任務的第一階段。 參考 來源《ecmascript6 入門》generator部分 認識generator函數 形式上,generator函數有兩個特點:一是functio...
摘要:迭代器的出現旨在消除這種復雜性并減少循環中的錯誤。返回一個迭代器,其值為集合的值。在迭代器中拋出錯誤除了給迭代器傳遞數據外,還可以給它傳遞錯誤條件。通過方法,當迭代器恢復執行時可令其拋出一個錯誤。 循環語句的問題 var colors = [red, green, blue]; for(var i=0; i= items.length); var value =...
閱讀 1524·2021-11-25 09:43
閱讀 4067·2021-11-15 11:37
閱讀 3199·2021-08-17 10:13
閱讀 3507·2019-08-30 14:16
閱讀 3538·2019-08-26 18:37
閱讀 2495·2019-08-26 11:56
閱讀 1135·2019-08-26 10:42
閱讀 613·2019-08-26 10:39