ES6 自定义迭代器如何做到像原生迭代器那样实现 Iterable 接口?

ES6 中原生实现了迭代器的数据结构可以像下面那样使用for of进行遍历

const arr = [ 1, 2, 3 ]
const iter = arr[Symbol.iterator]()
for (const item of iter) {
    console.log(item) // 1 2 3
}
const iter2 = iter[Symbol.iterator]()
console.log(iter === iter2) // true
const iter3 = iter[Symbol.iterator]()[Symbol.iterator]()[Symbol.iterator]()
console.log(iter === iter3) // true

问题是我自定义了一个迭代器,如何像原生迭代器那样实现Iterable接口来做到像上面那样使用 for of?

class Counter {
  constructor(limit) {
    this.limit = limit
  }
  [Symbol.iterator]() {
    let count = 1,
        limit = this.limit
    return {
      next() {
        if (count <= limit) {
          return { done: false, value: count++ }
        } else {
          return { done: true, value: undefined }
        }
      },
      [Symbol.iterator]() {
        // 这里该如何实现?
      }
    }
  }
}

const counter = new Counter(5)
const iter = counter[Symbol.iterator]()
const iter2 = iter[Symbol.iterator]()
console.log(iter === iter2) // false

for (const item of iter) { // Undefined is not a function
    // Some Code
}
阅读 1.7k
3 个回答
class Counter {
    constructor(limit) {
        this.limit = limit;

        // 把迭代器返回结果的迭代器指向迭代器[滑稽]
        this._iteratorResult[Symbol.iterator] = this[Symbol.iterator]
    }

    _iteratorResult = {
        next:() => {
            let count = 1,
                limit = this.limit
            if (count <= limit) {
                return { done: false, value: count++ }
            } else {
                return { done: true, value: undefined }
            }
        }
    };

    // 使用箭头函数,确保迭代器的 this 指向实例
    // 当然也可以在构造函数里赋值的时候用 bind 函数绑定
    [Symbol.iterator] = () => {
        return this._iteratorResult
    }
}

用生成器,而且对象有迭代器的话for...of会自动执行的,不需要显式的去执行

class Counter {
  constructor(limit) {
    this.limit = limit
  }
  *[Symbol.iterator]() {
    let count = 1,
        limit = this.limit;
    while(count<=limit) {
        yield count++
    }
  }
}
var counter = new Counter(5)
for (var v of counter) { // Undefined is not a function
    // Some Code
    console.log(v)
}

之前还没意识到这个问题,这说明内置可迭代对象返回的迭代器也是个可迭代对象(额,有点绕~)并且两者的迭代器是相等的,回到你自定义的Counter,这样修改:

class Counter {
  constructor(limit) {
    this.limit = limit
  }
  [Symbol.iterator]() {
    let count = 1,
        limit = this.limit,
        iterator = {
          next() {
            if (count <= limit) {
              return { done: false, value: count++ }
            } else {
              return { done: true, value: undefined }
            }
          },
          [Symbol.iterator]() {
            return iterator;
          }
        };
        
    return iterator;
  }
}

const counter = new Counter(5)
const iter = counter[Symbol.iterator]()
const iter2 = iter[Symbol.iterator]()
console.log(iter === iter2) // true
const iter3 = iter[Symbol.iterator]()[Symbol.iterator]()[Symbol.iterator]()
console.log(iter === iter3) // true
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题