# 迭代器的定义
迭代器(iterator),是确使用户可在容器对象(container,例如链表或数组)上遍访的对象,使用该接口无需关心对象的内部实现细节
其行为像数据库中的光标,迭代器最早出现在1974年设计的CLU编程语言中;
在各种编程语言的实现中,迭代器的实现方式各不相同,但是基本都有迭代器,比如Java、Python等;
从迭代器的定义我们可以看出来,迭代器是帮助我们对某个数据结构进行遍历的对象。 在JavaScript中,迭代器也是一个具体的对象,这个对象需要符合迭代器协议(iterator protocol):
迭代器协议定义了产生一系列值(无论是优先还是无限个)的标准方式;
那么在js中这个标准就是一个特定的next方法;
next方法有如下的要求:
一个无参数函数,返回一个应当拥有以下两个属性的对象:
done(boolean)
如果迭代器可以产生序列中的下一个值,则为false。(这等价于没有指定 done这个属性。)
如果迭代器已将序列迭代完毕,则为true。这种情况下,value是可选的,如果它依然存在,即为迭代结束之后的默认返回值。
value
- 迭代器返回的任何JavaScript值。done为true 时可省略。
# 实现一个简单迭代器
const names = ["okarin", "retr0", "mayuri"]
let index = 0
const namesIterator = {
next: function () {
if (index < names.length) {
return { done: false, value: names[index++] }
} else {
return { done: true, value: undefined }
}
},
}
for (let i = 0; i < 5; i++) {
console.log(namesIterator.next())
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 迭代器函数
const names = ["okarin", "retr0", "mayuri"]
function arrIterator(arr) {
let index = 0
return {
next: function () {
if (index < arr.length) {
return { done: false, value: arr[index++] }
} else {
return { done: true, value: undefined }
}
},
}
}
const nameIterator = arrIterator(names)
console.log(nameIterator.next())
console.log(nameIterator.next())
console.log(nameIterator.next())
console.log(nameIterator.next())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 可迭代对象
但是上面的代码整体来说看起来是有点奇怪的:
- 我们获取一个数组的时候,需要自己创建一个index变量,再创建一个所谓的迭代器对象
- 事实上我们可以对上面的代码进行进一步的封装,让其变成一个可迭代对象;
什么又是可迭代对象呢?
- 它和迭代器是不同的概念;
- 当一个对象实现了iterable protocol协议时,它就是一个可迭代对象;
- 这个对象的要求是必须实现@@iterator方法,在代码中我们使用Symbol.iterator 访问该属性;
const iterableObj = {
names: ["abc", "cba", "nba"],
[Symbol.iterator]: function () {
let index = 0
return {
next: () => {
if (index < this.names.length) {
return { done: false, value: this.names[index++] }
} else {
return { done: true, value: undefined }
}
},
}
},
}
const iterator = iterableObj[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
const iterator1 = iterableObj[Symbol.iterator]()
console.log(iterator1.next())
console.log(iterator1.next())
console.log(iterator1.next())
console.log(iterator1.next())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
对象可以使用in操作符进行遍历,可以通过in操作符拿到对象的属性名。
const obj = {
name:"okarin",
age:18
}
for(let i in obj){
console.log(i,obj[i])
}
//name okarin
//age 18
2
3
4
5
6
7
8
9
10
但是不能使用of操作符进行遍历,会提示:obj不是一个可迭代对象:
const obj = {
name:"okarin",
age:18
}
for(let i of obj){
console.log(i)
}
//TypeError: obj is not iterable
2
3
4
5
6
7
8
9
10
11
12
对可迭代对象进行of遍历:
const iterableObj = {
names: ["abc", "cba", "nba"],
[Symbol.iterator]: function () {
let index = 0
return {
next: () => {
if (index < this.names.length) {
return { done: false, value: this.names[index++] }
} else {
return { done: true, value: undefined }
}
},
}
},
}
for(let i of iterableObj){
console.log(i)
}
//abc
//cba
//nba
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
of操作符相当于一种next操作的语法糖,当得到的对象的 done属性为 false
时,就把value
赋值到 i
里面:
{ done: false, value: 'abc' }
{ done: false, value: 'cba' }
{ done: false, value: 'nba' }
{ done: true, value: undefined }
2
3
4
在done属性变为true
时,就停止遍历。
# 原生迭代器对象
事实上我们平时创建的很多原生对象已经实现了可迭代协议,会生成一个迭代器对象的:
String, Array, Map, Set, arguments对象、NodeList集合;
const names = ["okarin", "kurisu", "mayuri"]
const iterator = names[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
for (i of names) {
console.log(i)
}
// { value: 'okarin', done: false }
// { value: 'kurisu', done: false }
// { value: 'mayuri', done: false }
// { value: undefined, done: true }
// okarin
// kurisu
// mayuri
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 可迭代对象的应用
那么这些东西可以被用在哪里呢?
- JavaScript中语法:for...of、展开语法(spread syntax)、yield*(后面讲)、解构赋值(Destructuring_assignment);
- 创建一些对象时:new Map([Iterable])、new WeakMap([iterable])、new Set([iterable])、new WeakSet([iterable]);
- 一些方法的调用:Promise.all(iterable)、Promise.race(iterable)、Array.from(iterable);
# for of
const iterableObj = {
names: ["okarin", "kurisu", "mayuri"],
[Symbol.iterator]: function () {
let index = 0
return {
next: () => {
if (index < this.names.length) {
return { done: false, value: this.names[index++] }
} else {
return { done: true, value: undefined }
}
},
}
},
}
for(let i of iterableObj){
console.log(i)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# spread syntax
const iterableObj = {
names: ["okarin", "kurisu", "mayuri"],
[Symbol.iterator]: function () {
let index = 0
return {
next: () => {
if (index < this.names.length) {
return { done: false, value: this.names[index++] }
} else {
return { done: true, value: undefined }
}
},
}
},
}
const newNames = [...iterableObj]
console.log(newNames)
//[ 'okarin', 'kurisu', 'mayuri' ]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
而对对象进行展开运算,不是通过迭代器实现的。ES8 (ES 2019)
const obj = { name: "okarin", age: 18 }
const newObj = { ...obj }
console.log(newObj)
//{ name: 'okarin', age: 18 }
2
3
4
5
# 解构语法
const [name1 ,name2] = iterableObj
console.log(name1,name2)
//okarin kurisu
2
3
而对对象进行解构,不是通过迭代器实现的。ES8 (ES 2019)
const obj = { name: "okarin", age: 18 }
const {name,age} = obj
console.log(name,age)
//okarin 18
2
3
4
5
# 创建一些其他对象时
const set = new Set(iterableObj)
const arr = Array.from(iterableObj)
console.log(set)
console.log(arr)
//Set { 'okarin', 'kurisu', 'mayuri' }
//[ 'okarin', 'kurisu', 'mayuri' ]
2
3
4
5
6
7
8
# Promise.all
Promise.all(iterableObj).then((res)=>{
console.log(res)
})
//[ 'okarin', 'kurisu', 'mayuri' ]
2
3
4
5
# 自定义类的可迭代性
class Webset {
constructor(name, web) {
this.name = name
this.web = web
}
getWebNum() {
return this.web.length
}
// *[Symbol.iterator]() {
// yield* this.web
// }
[Symbol.iterator]() {
let index = 0
return {
next: () => {
if (index < this.web.length) {
return { done: false, value: this.web[index++] }
} else {
return { done: true, value: undefined }
}
},
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 什么是生成器?
生成器是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。
平时我们会编写很多的函数,这些函数终止的条件通常是返回值或者发生了异常。
生成器函数也是一个函数,但是和普通的函数有一些区别: 首先,生成器函数需要在function的后面加一个符号
*
其次,生成器函数可以通过yield关键字来控制函数的执行流程:
最后,生成器函数的返回值是一个Generator(生成器):
生成器事实上是一种特殊的迭代器;
MDN : Instead, they return a special type of iterator, called a Generator.
function* foo() {
console.log("函数开始执行~")
const value1 = 100
console.log(value1)
yield
const value2 = 200
console.log(value2)
yield
const value3 = 300
console.log(value3)
yield
console.log("函数执行结束~")
}
const generator = foo()
generator.next()
generator.next()
generator.next()
generator.next()
// 函数开始执行~
// 100
// 200
// 300
// 函数执行结束~
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 生成器函数的执行流程
# next的返回值
const generator = foo()
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
//函数开始执行~
//100
//{ value: undefined, done: false }
//200
//{ value: undefined, done: false }
//300
//{ value: undefined, done: false }
//函数执行结束~
//{ value: undefined, done: true }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
当遇到yield会暂停执行,而return会终止执行
# 通过yield返回值
在yield后面添加值 可以返回
function* foo() {
console.log("函数开始执行~")
const value1 = 100
console.log(value1)
yield value1
const value2 = 200
console.log(value2)
yield value2
const value3 = 300
console.log(value3)
yield value3
console.log("函数执行结束~")
return "done"
}
const generator = foo()
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
// 函数开始执行~
// 100
// { value: 100, done: false }
// 200
// { value: 200, done: false }
// 300
// { value: 300, done: false }
// 函数执行结束~
// { value: 'done', done: true }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 生成器的其他方法使用
# 生成器上的next方法可以传递参数
function* foo() {
console.log("函数开始执行~")
const value1 = 100
console.log(value1)
const n = yield value1
const value2 = 200 * n
console.log(value2)
yield value2
const value3 = 300
console.log(value3)
yield value3
console.log("函数执行结束~")
return "done"
}
const generator = foo()
console.log(generator.next())
console.log(generator.next(10))
console.log(generator.next())
console.log(generator.next())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 生成器的return终止执行
function* foo() {
console.log("函数开始执行~")
const value1 = 100
console.log(value1)
const n = yield value1
const value2 = 200 * n
console.log(value2)
yield value2
const value3 = 300
console.log(value3)
yield value3
console.log("函数执行结束~")
return "done"
}
const generator = foo()
console.log(generator.next())
console.log(generator.return(10))
console.log(generator.next())
console.log(generator.next())
// 函数开始执行~
// 100
// { value: 100, done: false }
// { value: 10, done: true }
// { value: undefined, done: true }
// { value: undefined, done: true }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 生成器的throw抛出异常
function* foo() {
console.log("函数开始执行~")
const value1 = 100
try {
yield value1
} catch (error) {
console.log("Error:", error)
}
const value2 = 200
yield value2
console.log("函数执行结束~")
}
const generator = foo()
console.log(generator.next())
console.log(generator.throw("error massage"))
console.log(generator.next())
// 函数开始执行~
// { value: 100, done: false }
// Error: error massage
// { value: 200, done: false }
// 函数执行结束~
// { value: undefined, done: true }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 生成器替代迭代器使用
//迭代器
function createArrayIterator(arr) {
let index = 0
return {
next: function () {
if (index < arr.length) {
return { done: false, value: arr[index++] }
} else {
return { done: true, value: undefined }
}
},
}
}
const names = ["okarin","kurisu","mayuri"]
const arrayIterator = createArrayIterator(names)
console.log(arrayIterator.next())
console.log(arrayIterator.next())
console.log(arrayIterator.next())
console.log(arrayIterator.next())
// { value: 'okarin', done: false }
// { value: 'kurisu', done: false }
// { value: 'mayuri', done: false }
// { value: undefined, done: true }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//生成器
function* createArrayIterator(arr) {
for (let item of arr) {
yield item
}
}
const names = ["okarin", "kurisu", "mayuri"]
const arrayIterator = createArrayIterator(names)
console.log(arrayIterator.next())
console.log(arrayIterator.next())
console.log(arrayIterator.next())
console.log(arrayIterator.next())
// { value: 'okarin', done: false }
// { value: 'kurisu', done: false }
// { value: 'mayuri', done: false }
// { value: undefined, done: true }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# yield*
相当于是一种yield的语法糖,只不过会依次迭代这个可迭代对象,每次迭代其中的一个值;
function* createArrayIterator(arr) {
yield* arr
}
const names = ["okarin", "kurisu", "mayuri"]
const arrayIterator = createArrayIterator(names)
console.log(arrayIterator.next())
console.log(arrayIterator.next())
console.log(arrayIterator.next())
console.log(arrayIterator.next())
// { value: 'okarin', done: false }
// { value: 'kurisu', done: false }
// { value: 'mayuri', done: false }
// { value: undefined, done: true }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
对某个范围迭代
function* createRangeIterator(start, end) {
let index = start
while (index < end) {
yield index++
}
}
const rangeIterator = createRangeIterator(1, 5)
for (let i = 0; i < 5; i++) {
console.log(rangeIterator.next())
}
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: false }
// { value: 4, done: false }
// { value: undefined, done: true }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18