【ES6腳丫系列】遍歷器iterator

『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接口

具備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()方法。

在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'
    // ...

使用while循環

有了遍歷器接口,數據結構就能夠用for...of循環遍歷,也可使用while循環遍歷。

var $iterator = ITERABLE[Symbol.iterator]();
var $result = $iterator.next();
while (!$result.done) {
    var x = $result.value; // ...
    $result = $iterator.next();
}

TypeScript的寫法

遍歷器接口(Iterable)、指針對象(Iterator)和next方法返回值的規格能夠描述以下。

interface Iterable {
    [Symbol.iterator](): Iterator, }

interface Iterator { next(value ? : any): IterationResult, }

interface IterationResult {
    value: any,
        done: boolean,
}

Iterator接口與Generator函數

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接口的場合

有一些場合會默認調用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*

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 }

其餘場合

因爲數組的遍歷會調用遍歷器接口,因此任何接受數組做爲參數的場合,其實都調用了遍歷器接口。

下面是一些例子。

  • for...of
  • Array.from()
  • Map(), Set(), WeakMap(), WeakSet()(好比new Map([['a',1],['b',2]]))
  • Promise.all([])
  • Promise.race([])

字符串的Iterator接口

字符串是一個相似數組的對象,原生具備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)
}
相關文章
相關標籤/搜索