Iterator
基本概念
- JavaScript数据集合:Array、Object、Map、Set;用户可自由组合,定于自己的数据结构
- 遍历器:
- 是一个为各种数据结构提供统一的访问机制的接口
- 拥有该接口,就可以对该数据结构按照某种次序进行遍历操作
- 当一个数据结构拥有
Symbol.iterator
属性(是一个函数)时,就默认他是可遍历的,执行该属性,就会返回一个遍历器;要使用遍历器的方法,比如next,必须执行该函数,原生具备该接口的也是如此 - 该接口主要供for...of使用,当使用for...of循环时,会自动寻找数据结构的该接口
- 遍历器对象必须具备next方法,而return()和throw()是可选的;其中,若for...of循环提前退出(出错、break、提前释放资源等),会调用return方法;return方法必须返回一个对象
- 原生具备遍历器接口的数据结构:不用任何处理就能被for...of遍历,有:
- Array
- String,是一个类数组对象
- Map
- Set
- 参数的arguments
- NodeList对象
- TypedArray
- 遍历器遍历过程:
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用该指针对象的next方法,会将指针指向该数据结构的第一个成员;之后依次类推
- 调用遍历器接口的场合:
- for...of
- 对数组/Set进行解构赋值时,会默认调用Symbol.iterator方法
- 扩展运算符,会默认调用Symbol.iterator方法:任何具备遍历器接口的数据结构,都可以通过扩展运算符转为数组,比如
str = 'hello'; [...str]
- yield*后面跟一个可遍历结构,会自动调用该结构的遍历器接口
- 由于数据的遍历会调用遍历器结构,所以任何接受数组作为参数的场合,都调用了遍历器接口,比如:
- for...of
- Array.from()
- Map(), Set(), WeakMap(), WeakSet()
- Promise.all(), Promise.race()等等
- 注意事项:
- 每一次调用next方法,都会返回当前成员的信息,即一个包含value(当前成员的值)和done(布尔值,是否遍历结束)属性的对象
- 对于遍历器对象来说,done: false和value: undefined是可以省略的🍀
- 对象若想被for....of遍历,必须要在Symbol.iterator属性上部署遍历器生成方法(原型上部署也行)🍤
- 类数组对象(含有数组键名和length属性),可用一个简单方法,部署遍历器接口,即:类数组对象的Symbol.iterator属性直接指向
Array.prototype[Symbol.iterator]
或者[][Symbol.iterator]
;而普通对象直接这样做,无任何效果 - 若Symbol.iterator返回的不是一个遍历器生成函数,解释引擎将会报错
- 有了遍历器接口,就能被for...of和while(通过done属性判断是否结束)循环遍历
- 可以覆盖原生的Symbol.iterator方法,修改原生的遍历行为
- 用for...of遍历数组,仅仅返回具有数字索引的属性;和for...in的结果不一样
- 并不是所有的类数组对象都具备iterator接口,可以使用Array.from()将其转为数组
- iterator的最简单实现,是使用generator函数,因为generator原生具备next方法,且调用next方法会返回一个含有done和value属性的对象🍇
- for...of循环可使用的范围有:
- 数组
- 字符串
- Set
- Map
- 类数组对象
- for...of注意事项:
- 可以与break、continue、return结合使用
- 提供了遍历所有数据结构的统一操作接口
- for...in循环:主要是用来遍历对象
- 只能遍历键名
- 遍历数组时,手动添加的非数字键也会被遍历
- Set和Map的遍历顺序是按照各个成员添加的顺序;Set遍历时返回一个值,Map遍历时返回一个含键名和键值的数组
- ES6中的数组、Map、Set的entries()、keys()、values()返回的都是一个遍历器对象
javascript
// 🍀:模拟遍历器对象:无限运行
function idMaker () {
let index = 0
return {
next () {
return {
value: index++,
done: false
}
}
}
}
// 🍤:对象部署遍历器接口
class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
}
// 部署该接口,返回对象本身
[Symbol.iterator]() { return this; }
// 由于返回对象本身,所以它必须拥有next方法,并返回一个含有done、value的对象
next() {
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
}
return {done: true, value: undefined};
}
}
function range(start, stop) {
return new RangeIterator(start, stop);
}
for (var value of range(0, 3)) {
console.log(value); // 0, 1, 2
}
// 🍤:原型链上部署遍历器接口
function Obj(value) {
this.value = value;
this.next = null;
}
Obj.prototype[Symbol.iterator] = function() {
var iterator = { next: next };
var current = this;
function next() {
if (current) {
var value = current.value;
current = current.next;
return { done: false, value: value };
}
return { done: true };
}
return iterator;
}
var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);
one.next = two;
two.next = three;
for (var i of one){
console.log(i); // 1, 2, 3
}
// 🍇:使用generator函数实现iterator接口
let myIterable = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
};
[...myIterable] // [1, 2, 3]
// 或者采用下面的简洁写法
let obj = {
* [Symbol.iterator]() {
yield 'hello';
yield 'world';
}
};
for (let x of obj) {
console.log(x);
}
// "hello"
// "world"
1
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98