8
头图
"Code tailor",为前端开发者提供技术相关资讯以及系列基础文章,微信关注“小和山的菜鸟们”公众号,及时获取最新文章。

前言

在开始学习之前,我们想要告诉您的是,本文章是对阮一峰《ECMAScript6 入门》一书中 "Iterator" 章节的总结,如果您已掌握下面知识事项,则可跳过此环节直接进入题目练习

  • 什么是 Iterator?有何作用?
  • Iterator 是如何遍历的?
  • 默认的 Iterator 的接口和常用场合有哪些?

如果您对某些部分有些遗忘,👇🏻 已经为您准备好了!

学习链接

Iterator 的学习

汇总总结

概念和作用

JavaScript 表示“集合”的数据结构,主要是数组(Array)、对象(Object),Map Set。用户可以组合使用它们,定义自己的数据结构,比如数组的成员是 MapMap 的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。

遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

作用:

  • 为各种数据结构,提供一个统一的、简便的访问接口
  • 使得数据结构的成员能够按某种次序排列
  • ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 进行遍历循环。

遍历过程

  1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
  2. 第一次调用指针对象的 next 方法,可以将指针指向数据结构的第一个成员。
  3. 第二次调用指针对象的 next 方法,指针就指向数据结构的第二个成员。
  4. 不断调用指针对象的 next 方法,直到它指向数据结构的结束位置。

默认 Iterator 接口

Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即 for...of 循环。当使用 for...of 循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”。

在 ES6 中有以下几个数据结构具有原生的 Iterator 接口,也就是说,这些数据结构可以直接调用 for...of 进行遍历

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

除了上面这些数据结构,如果需要在别的数据结构中(主要为 Object)调用 for...of,则需要自己在 Symbol.iterator 属性上面部署,这样才会被 for...of 循环遍历。定义方式举例如下:

const obj = {
  [Symbol.iterator]: function () {
    return {
      next: function () {
        return {
          value: 1,
          done: true,
        }
      },
    }
  },
}

调用 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']
  • 扩展运算符
// 例一
var str = 'hello'
[...str] //  ['h','e','l','l','o']

// 例二
let arr = ['b', 'c']
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']
  • 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 }

了解遍历器如何写

遍历器对象除了具有 next() 方法,还可以具有 return() 方法和 throw() 方法。如果你自己写遍历器对象生成函数,那么 next()方法是必须部署的,return() 方法和 throw() 方法是否部署是可选的。
/*
return()方法的使用场合是,如果for...of循环提前退出(通常是
因为出错,或者有break语句),就会调用return()方法。如果一个对
象在完成遍历前,需要清理或释放资源,就可以部署return()方法。
*/
function readLinesSync(file) {
  return {
    [Symbol.iterator]() {
      return {
        next() {
          return { done: false }
        },
        return() {
          file.close()
          return { done: true }
        },
      }
    },
  }
}

题目自测

一: for in 和 for of 的区别


二: 以下代码会输出什么()

const obj = { 2: 5, 3: 6, 4: 7 }
obj[Symbol.iterator] = function () {
  return {
    next: function () {
      if (this._countDown === 3) {
        return { value: this._countDown, done: true }
      }
      this._countDown = this._countDown + 1
      return { value: obj[this._countDown], done: false }
    },
    _countDown: 0,
  }
}
for (const i of obj) {
  console.log(i)
}
  • A. 2,3,4
  • B. 5,6,7
  • C. undefined,5,6
  • D. TypeError: obj is not iterable

题目解析

一、

Answer:

正确答案及解析:

for...infor...of
Applies toEnumerable PropertiesIterable Collections
Use with Objects?YesNo
Use with Arrays?Yes, but not advisedYes
Use with Strings?Yes, but not advisedYes

二、

Answer:(C)

obj 是一个普通对象,正常遍历会报错如 D,但上面代码中添加了 Symbol.iterator 属性,所以可以遍历,遍历时看 return 的值为 obj[this._countDown] ,及把每次遍历次数当做键名取值,可以看得到,obj 中只有 2,3,4 这三个键名的键,所以第一次取到的是 undefined,后面得到 5 和 6 所以选 C


小和山的菜鸟们
377 声望2.1k 粉丝

每日进步的菜鸟,分享前端学习手册,和有心学习前端技术的小伙伴们互相探讨,一同成长。