侧边栏壁纸
  • 累计撰写 218 篇文章
  • 累计创建 59 个标签
  • 累计收到 5 条评论

可迭代与迭代器

barwe
2022-04-03 / 0 评论 / 0 点赞 / 846 阅读 / 2,365 字
温馨提示:
本文最后更新于 2022-04-03,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

迭代协议(Iteration Protocols)是 ESMAScript 2015 (ES6) 的补充内容,它还只是一种 约定,还没有成为内置语法。

任何对象都可以按照约定实现迭代协议。

迭代协议包含两个内容:可迭代协议(Iterable)和 迭代器协议(Iterator)。

可迭代协议

要想使一个普通对象成为 可迭代 的对象,只需要给它添加一个 [Symbol.iterator] 属性。

该属性值是一个函数,无传入参数且返回一个 迭代器(Iterator)。

只有可迭代对象才能用 for ... of 迭代取值。

迭代器协议

要想使一个对象成为 迭代器(Iterator),只需要给它实现 next() 方法。

该方法接受零个或者一个参数,返回一个至少包含 donevalue 两个属性的对象:

  • done (boolean) 标志序列是否迭代完了,迭代完成之后应该返回 true
  • value 迭代器的返回值,在 donetrue 时可以忽略,此时为 undefined

二者的区别和关系

对象实现了 可迭代协议,说明它可以被迭代,例如 for ... of,但是并没有说明每次迭代应该怎么返回值,以及返回什么值。

可迭代协议[Symbol.iterator] 属性返回的是一个 迭代器,迭代器负责在每次迭代时生成具体的值。

实现了 next() 方法的对象叫做 迭代器,通过调用其 next() 方法可以迭代获取序列的值。

for ... of 等消费可迭代对象的语法就是通过调 可迭代对象 关联的 迭代器next() 方法实现遍历的。

迭代协议的例子

数组的原型对象实现了 可迭代协议

Array.prototype[Symbol.iterator]
// ƒ values() { [native code] }

Array.prototype[Symbol.iterator]()
// Array Iterator {}

手动使用数组对象的迭代器对序列进行迭代:

const x = [1, 2, 3]
const iterator = x[Symbol.iterator]()
iterator.next()
// {value: 1, done: false}
iterator.next()
// {value: 2, done: false}
iterator.next()
// {value: 3, done: false}
iterator.next()
// {value: undefined, done: true}

可以看出,数组对象 x 是一个 可迭代对象x[Symbol.iterator] 是一个 迭代器

此外,String, TypedArray, SetMap 也都默认实现了可迭代协议和迭代器协议。

一个对象可以同时实现可迭代协议和迭代器协议,即它既是可迭代对象,又是迭代器:

const myIterableIterator = {
  next: function() { /*...*/ },
  [Symbol.iterator]: function() { return this }
}

但是一般不建议这么干。

扩展语法(...)也会自动调用待扩展对象的迭代器取出序列:

console.log([...x])

迭代器的例子

只有满足 迭代器协议 的约定,可以用多种方法创建迭代器。

例如通过保存索引位置,创建指定序列的迭代器:

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

const it = makeIterator([1, 2, 3])
console.log(it.next()) // {value: 1, done: false}
console.log(it.next()) // {value: 2, done: false}
console.log(it.next()) // {value: 3, done: false}
console.log(it.next()) // {value: undefined, done: true}

例如创建可以无限迭代的迭代器:

function makeInfiniteIterator() {
  let index = 0
  return {
    next: function() {
      return { done: false, value: index++ }
    }
  }
}

例如通过生成器函数创建迭代器:

function* makeSimpleGenerator(array) {
  let index = 0;
  while (index < array.length) {
    yield array[index++];
  }
}

function* makeInfiniteGenerator() {
  let index = 0;
  yield index++;
}

通过类实现迭代器(ES6):

class SimpleIterator {
  constructor(arr) {
    this.arr = arr;
  }
  
  [Symbol.iterator]() {
    let index = 0;
    return {
      // 因为需要用到 this,所以用箭头函数
      next: () => {
        return index < this.arr.length ? { done: false, value: this.arr[index++] } : { done: true };
      }
    }
  }
}

生成器与迭代器/可迭代对象的关系

生成器 既是可迭代对象,又是迭代器。

const g = function* () {
  yield 1;
  yield 2;
}();

console.log(g === g[Symbol.iterator]()) // true

Reference

👉 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols

0

评论区