"Code tailor",为前端开发者提供技术相关资讯以及系列基础文章,微信关注“小和山的菜鸟们”公众号,及时获取最新文章。
前言
在开始学习之前,我们想要告诉您的是,本文章是对阮一峰《ECMAScript6 入门》一书中 "Iterator" 章节的总结,如果您已掌握下面知识事项,则可跳过此环节直接进入题目练习
- 什么是 Iterator?有何作用?
- Iterator 是如何遍历的?
- 默认的 Iterator 的接口和常用场合有哪些?
如果您对某些部分有些遗忘,👇🏻 已经为您准备好了!
学习链接
汇总总结
概念和作用
JavaScript
表示“集合”的数据结构,主要是数组(Array
)、对象(Object
),Map
和 Set
。用户可以组合使用它们,定义自己的数据结构,比如数组的成员是 Map
,Map
的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
遍历器(Iterator
)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator
接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
作用:
- 为各种数据结构,提供一个统一的、简便的访问接口
- 使得数据结构的成员能够按某种次序排列
- ES6 创造了一种新的遍历命令
for...of
循环,Iterator 接口主要供for...of
进行遍历循环。
遍历过程
- 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
- 第一次调用指针对象的
next
方法,可以将指针指向数据结构的第一个成员。 - 第二次调用指针对象的
next
方法,指针就指向数据结构的第二个成员。 - 不断调用指针对象的
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...in | for...of | |
---|---|---|
Applies to | Enumerable Properties | Iterable Collections |
Use with Objects? | Yes | No |
Use with Arrays? | Yes, but not advised | Yes |
Use with Strings? | Yes, but not advised | Yes |
二、
Answer:(C)
obj 是一个普通对象,正常遍历会报错如 D,但上面代码中添加了 Symbol.iterator
属性,所以可以遍历,遍历时看 return
的值为 obj[this._countDown]
,及把每次遍历次数当做键名取值,可以看得到,obj 中只有 2,3,4 这三个键名的键,所以第一次取到的是 undefined,后面得到 5 和 6 所以选 C
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。