es6-Iterator

72 阅读2分钟

Iterator

迭代器,es6引入的一种遍历机制,为我们遍历数据提供一个统一的接口。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

本质

Iterator是一个指针,指向当前数据结构的起始位置。当调用next()时,指针往后移,指向下一个对象;不断调用指针对象的next方法,直到它指向数据结构的结束位置。 每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,是一个包含value和done两个属性的对象。value是当前成员的值,done是一个布尔值,表示遍历是否结束。

const items = ["1", "2", "3"];
const it = items[Symbol.iterator]();

it.next();
//{value: "1", done: false}
it.next();
//{value: "2", done: false}
it.next();
//{value: "3", done: false}
it.next();
//{value: undefined, done: true}

下面写一个类似的例子:

function myIterator(arr) {
  let nextIdx = 0;
  return {
    next: function() {
      return nextIdx < arr.length ?
        {value: arr[nextIdx++], done: false} :
        {value: undefined, done: true};
    }
  };
}

let it = myIterator(['a', 'b', 'c']);

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: "c", done: false }
it.next() // { value: undefined, done: true }

支持迭代的数据结构

es6规定默认的Iterator接口部署在Symbol.iterator属性,也就是说只要一个数据结构有Symbol.iterator属性的话,它就是可遍历的;支持for...of循环。 原生具有Symbol.iterator的数据结构有:

  • Array
  • Set
  • Map
  • String
  • arguments(参数列表)
  • NodeList(dom节点)
let str = 'str'
for(let i of str) {
    console.log(i) // s t r
}
let iter = str[Symbol.iterator]();

iter.next() // { value: 's', done: false }
iter.next() // { value: 't', done: false }
iter.next() // { value: 'r', done: false }
iter.next() // { value: undefined, done: true }

可以利用Generator快速实现个Symbol.iterator

let myIterable = { 
    [Symbol.iterator]: function* () { 
        yield 1;
        yield 2;
        yield 3; 
    } 
 };
 [...myIterable] // [1, 2, 3]

注意:对象不具有Symbol.iterator属性,因为对象的哪个属性先遍历,哪个属性后遍历是不确定的。

让对象支持for...of遍历

let obj = {
    '0': 's',
    '1': 't',
    length: '2',
    [Symbol.iterator]: Array.prototype[Symbol.iterator],
}
for(let i of obj) {
    console.log(i) // s t
}

上面的例子对象是有序列的、线性的,如果是一般对象的话可以支持吗

function* objIterate(obj) {
    var keys = Object.keys(obj);
    for (var i=0; i<keys.length; i++) {
        yield keys[i]
    }
}


let obj = {
    1: '1',
    b: 'b',	
    3: '3',
    a: 'a',
    c: 'c'
    2: '2',
    
};

for (var key of objIterate(obj)) {
    console.log(key);
}
// 1 2 3 b a c

我们发现打印的顺序乱了,数字属性按顺序排了起来,而常规的一些属性并没有。说明一般对象并不适合使用迭代器。