『ES6知識點總結』遍歷器iterator
本文內容以下:數組
1 具備iterator接口的數據結構 2 遍歷器過程 3 遍歷器做用: 4 模擬next()方法 5 使用while循環 6 TypeScript的寫法 7 Iterator接口與Generator函數 8 對象的遍歷器接口 8.1 對於相似數組的對象 9 調用Iterator接口的場合 9.1 解構賦值 9.2 擴展運算符 9.3 yield* 9.4 其餘場合 10 字符串的Iterator接口
【01】原生就具備Iterator接口,它們的遍歷器接口部署在Symbol.iterator屬性上:數據結構
Array,Object(類數組對象),Map,WeakMap,Set,WeakSet,字符串。函數
let arr = new Array();prototype
let iterator =arr[Symbol.iterator]();指針
【02】其餘數據結構(主要是對象)的Iterator接口,都須要本身部署在Symbol.iterator屬性上面。(原型鏈上的對象具備該方法也可)。rest
【03】一個數據結構只要具備Symbol.iterator屬性,就能夠認爲是「可遍歷的」(iterable)。
就稱爲部署了遍歷器接口。就能夠遍歷全部成員。可經過for of 遍歷。code
【04】遍歷器對象自己也有Symbol.iterator方法,執行後返回自身。對象
gen是一個Generator函數,調用它會生成一個遍歷器對象g。它的Symbol.iterator屬性,也是一個遍歷器對象生成函數,執行後返回它本身。接口
function* gen(){ // some code } var g = gen(); g[Symbol.iterator]() === g // true
【01】zyx456:說白了,等於這些數據結構有一個方法(PS,這個方法稱爲遍歷器函數)。ip
方法名是[Symbol.iterator],方法中返回一個對象(PS,這個對象稱爲遍歷器對象),該對象有一個next()方法。這個next()會遍歷數據結構的成員。返回{value:XX||undefined , done:true||false}
let obj = {
[Symbo.iterator] (){
return {next(){}}
}
}
調用遍歷器函數,就會返回一個遍歷器對象。
每次調用iterator.next(),就使用該數據結構的一個成員,成員的值就是返回對象中value的值。done爲false,最後會返回一個有value和done兩個屬性的對象。
從頭至尾依次使用數據結構的成員。直到使用的成員都訪問完了。
當數據結構的成員已所有訪問了,此時,再調用iterator.next()。返回的對象中,value的值爲undefined,done爲true。
done屬性是一個布爾值,表示遍歷是否結束。
let arr = [1,2,3];
let iterator = arr[Symbol.iterator]();
iterator.next();//{value:"",done:false};
let arr = ['a', 'b', 'c']; let iter = arr[Symbol.iterator]();//返回遍歷器對象。 iter.next() // { value: 'a', done: false } iter.next() // { value: 'b', done: false } iter.next() // { value: 'c', done: false } iter.next() // { value: undefined, done: true }
【03】遍歷器與它所遍歷的那個數據結構是分開的。
一是爲各類數據結構提供一個統一的訪問接口;
二是讓數據結構的成員能夠按某種次序排列;
三是供for...of使用。
使用一個函數,函數參數是數組,返回一個對象,對象具備next()方法。
在next()中,使用一個變量,遍歷數組的下標。經過和數組的長度對比,來判斷返回不一樣的對象。
返回對象都有value和done屬性。
function Convert(arr){ let index = 0; return { next (){ if(index<arr.length){ return {value:arr[index++],done:false} } else { return {value:undefined,done:true} } } } } var it = Convert(['a', 'b']); it.next() // { value: "a", done: false } it.next() // { value: "b", done: false } it.next() // { value: undefined, done: true }
zyx456:next返回的就是指針對象了。此時,it是對象,它的變量index是保存在函數中的。
對於遍歷器對象來講,done: false和value: undefined屬性都是能夠省略的,所以上面的Covert函數能夠簡寫成下面的形式。
function Covert(array) { var nextIndex = 0; return { next: function() { return nextIndex < array.length ? { value: array[nextIndex++] } : { done: true }; } } }
【】下面是一個無限運行的遍歷器對象的例子。
zyx456:僅瞭解。實用性不大。
遍歷器生成函數idMaker,返回一個遍歷器對象(即指針對象)。可是並無對應的數據結構,或者說,遍歷器對象本身描述了一個數據結構出來。
function idMaker() { let index = 0; return { next: function() { return { value: index++, done: false }; } } }var it = idMaker(); it.next().value // '0' it.next().value // '1' it.next().value // '2' // ...
有了遍歷器接口,數據結構就能夠用for...of循環遍歷,也可使用while循環遍歷。
var $iterator = ITERABLE[Symbol.iterator](); var $result = $iterator.next(); while (!$result.done) { var x = $result.value; // ... $result = $iterator.next(); }
遍歷器接口(Iterable)、指針對象(Iterator)和next方法返回值的規格能夠描述以下。
interface Iterable { [Symbol.iterator](): Iterator, } interface Iterator { next(value ? : any): IterationResult, } interface IterationResult { value: any, done: boolean, }
var myIterable = {}; myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; [...myIterable] // [1, 2, 3] // 或者採用下面的簡潔寫法 let obj = { * [Symbol.iterator]() { yield 'hello'; yield 'world'; } }; for (let x of obj) { console.log(x); } // hello // world
【】對象部署遍歷器接口並非很必要,可使用Map。
(存在數值鍵名和length屬性),部署Iterator接口,有一個簡便方法,就是Symbol.iterator方法直接引用數組的Iterator接口。
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; // 或者 NodeList.prototype[Symbol.iterator] = [][Symbol.iterator]; [...document.querySelectorAll('div')] // 能夠執行了
下面是相似數組的對象調用數組的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接口(即Symbol.iterator方法)。
對數組和Set結構進行解構賦值時,會默認調用Symbol.iterator方法。
let set = new Set().add('a').add('b').add('c'); let [x, y] = set; // x='a'; y='b' let [first, ...rest] = set; // first='a'; rest=['b','c'];
擴展運算符(...)會內部調用默認的iterator接口。
能夠將任何部署了Iterator接口的數據結構,轉爲數組。
// 例一 var str = 'hello'; [...str] // ['h','e','l','l','o'] // 例二 let arr = ['b', 'c']; ['a', ...arr, 'd'] // ['a', 'b', 'c', 'd']
let arr = [...iterable];
yield*後面跟的是一個可遍歷的結構,它會調用該結構的遍歷器接口。
let generator = function* () { yield 1; yield* [2,3,4]; yield 5;};var iterator = generator(); iterator.next() // { value: 1, done: false } iterator.next() // { value: 2, done: false } iterator.next() // { value: 3, done: false } iterator.next() // { value: 4, done: false } iterator.next() // { value: 5, done: false } iterator.next() // { value: undefined, done: true }
因爲數組的遍歷會調用遍歷器接口,因此任何接受數組做爲參數的場合,其實都調用了遍歷器接口。
下面是一些例子。
字符串是一個相似數組的對象,原生具備Iterator接口。
var someString = "hi"; typeof someString[Symbol.iterator] // "function" var iterator = someString[Symbol.iterator](); iterator.toString(); // => '[object String Iterator]' iterator.next() // { value: "h", done: false } iterator.next() // { value: "i", done: false } iterator.next() // { value: undefined, done: true }
let some = "book"; for (let i of some){ console.log(i) }