(六) Iterator 迭代器

1. 迭代、可迭代对象

1.1 迭代 iteration

维基百科给出的定义是: 迭代是重复反馈过程的活动, 每一次对过程的重复被称为一次迭代

在编程语言中, 我们通过像 for 循环的方式来遍历某个数据, 这种遍历我们称为迭代 (iterator)

for (let i = 0; i < 5; i++) {
  // 每一次打印输出就是一次迭代
  console.log(i)
}

1.2 可迭代对象

对象

在学习python的过程中, 我了解到python中的万物皆对象, 后来在刚开始学习js的过程中, 在某些博客/帖子中看到关于js中万物皆对象的描述, 随着学习时间的推移, 对js一些机制的逐步学习了解, 才发现所谓的万物皆对象是错误的说法

所以这里针对js描述的可迭代对象中的对象二字, 我们暂且将它看作是一类目标事物, 而不是object类型

可迭代对象

根据上面迭代的概念, 我们暂且可迭代对象定义为: 一类可进行遍历的事物, 在es6之前, 他们是

  • 字符串
  • 数组
  • 类数组

2. 迭代器 iterator

2.1 为什么需要迭代器

当我们去遍历一个数组时, 我们可以通过像for循环, forEach等方法, 遍历对象(键)时可以通过for...in , 遍历Map、Set时可以通过forEach

但是, 你有没有发现, 我们遍历不同的数据类型, 要用不同的方式, 是不是有点不够统一, 我遍历个数据,还要转换, 封装, 甚至有可能还要操作原型, 烦不烦? 既然如此, 那为什么不设计一个通用的方式, 让不同的数据结构都可以通过我这一种方式来完成遍历呢 ?这就是ES6设计迭代器的原因 (我猜是)

2.2 什么是迭代器

迭代器 iterator 是为各种不同的数据结构提供的统一访问机制, 是专门用于迭代的对象, 并带有特定接口

  • 迭代器是一个对象
  • 内部的特定接口专门处理迭代过程

2.3 迭代器的内部接口是什么

es6规定: 所有的迭代器对象都有一个next()方法, 该方法的返回值是一个结果对象, 结果对象有两个属性: value和done, 前者表示当前成员值, 后者是一个标识, 用于标识原数据集是否已经遍历完, 这个next()方法就是接口

也是用到了闭包

// 利用es5的语法来实现一个迭代器, 调用createIterator返回的对象是迭代器
function createIterator(items) {
  var i = 0;
  return {
    next: function () {
      var done = (i >= items.length);
      var value = !done ? items[i++] : undefined;
      return {
        done: done,
        value: value
      }
    }
  }
}
var iterator = createIterator([1, 2, 3])
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

2.4 这个接口是干什么的

上面提到: 接口是专门用来处理迭代过程的, 通过代码演示也可以发现, 通过不断的调用next() 方法, 可以遍历出数组的成员, 但是显然, 迭代器接口不是让我们手动的一次次的去调用它的, 所以es6定义了一些专门调用这个迭代器接口的方法, 比如: for...of

当我们使用for...of遍历某种数据结构时, 该方法就会自动去寻找该数据结构上的迭代器接口

  • 注意看仔细: 迭代器接口是定义在数据结构上的 !

而一旦某种数据结构上定义了迭代器接口, 我们就称这种数据结构是 可迭代的 (iterable)

知道了这个, 还有一个问题, 那就是迭代器接口是怎么定义在数据结构上的 ?

  • es6规定, 默认的迭代器接口是定义在数据结构的 Symbol.iterator 属性(该属性是一个方法)上的,

也就是说, 一个数据结构只要定义了 Symbol.iterator 属性, 它就是 可迭代的


@我们将上面的捋一捋

  • 迭代器是一个对象
  • 迭代器接口是迭代器内部实现迭代的方法
  • for...of方法内部实现原理是调用迭代器接口
  • 而迭代器接口被定义在数据结构的Symbol.iterator属性上

那么也就意味着, 只要你有 Symbol.iterator属性, 那你就是一个可迭代对象, 那你就可以通过for...of遍历

所以, 最开始提到的可迭代对象的概念以及哪些是可迭代对象, 我们应该重新定义一下了:

  • 可迭代对象是含有Symbol.iterator属性的数据结构, 他们分别是
    • 字符串
    • 数组
    • 类数组 (arguments / NodeList => DOM)
    • Set
    • Map

对象不具备迭代器接口

3. for...of

我们来看一看for...of方法, 上面说了, for...of通过调用迭代器接口完成遍历操作

示例

let arr = [1, 2, 3]
let str = 'hello world'
let set = new Set([4, 5, 6])
let map = new Map([['key', 'value']])

// 遍历数组
for (const i of arr) {
  console.log(i)
}

// 遍历字符串
for (const i of str) {
  console.log(i)
}

// 遍历Set
for (const i of set) {
  console.log(i);
}

// 遍历Map
for (const i of map) {
  console.log(i)
}

// 遍历类数组 DOM的NodeList对象
let domList = document.querySelectorAll('li')
for (const i of domList) {
  console.log(i)
}

是不是就是我们一开始所说的那个 统一的方法

posted @ 2021-08-02 22:38  只猫  阅读(76)  评论(0编辑  收藏  举报