1
- # Iterator和for ...of循环
1
+ # Iterator 和 for ...of 循环
2
2
3
3
## Iterator(遍历器)的概念
4
4
5
- JavaScript原有的表示 “集合”的数据结构,主要是数组(Array)和对象(Object),ES6又添加了Map和Set 。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象 。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
5
+ JavaScript 原有的表示 “集合”的数据结构,主要是数组(` Array ` )和对象(` Object ` ),ES6 又添加了 ` Map ` 和 ` Set ` 。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是 ` Map ` , ` Map ` 的成员是对象 。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
6
6
7
7
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
8
8
9
- Iterator的作用有三个 :一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令` for...of ` 循环,Iterator接口主要供` for...of ` 消费。
9
+ Iterator 的作用有三个 :一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令` for...of ` 循环,Iterator接口主要供` for...of ` 消费。
10
10
11
- Iterator的遍历过程是这样的 。
11
+ Iterator 的遍历过程是这样的 。
12
12
13
13
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
14
14
@@ -64,7 +64,7 @@ function makeIterator(array) {
64
64
}
65
65
```
66
66
67
- 由于Iterator只是把接口规格加到数据结构之上 ,所以,遍历器与它所遍历的那个数据结构,实际上是分开的,完全可以写出没有对应数据结构的遍历器对象,或者说用遍历器对象模拟出数据结构。下面是一个无限运行的遍历器对象的例子。
67
+ 由于 Iterator 只是把接口规格加到数据结构之上 ,所以,遍历器与它所遍历的那个数据结构,实际上是分开的,完全可以写出没有对应数据结构的遍历器对象,或者说用遍历器对象模拟出数据结构。下面是一个无限运行的遍历器对象的例子。
68
68
69
69
``` javascript
70
70
var it = idMaker ();
@@ -87,9 +87,7 @@ function idMaker() {
87
87
88
88
上面的例子中,遍历器生成函数` idMaker ` ,返回一个遍历器对象(即指针对象)。但是并没有对应的数据结构,或者说,遍历器对象自己描述了一个数据结构出来。
89
89
90
- 在ES6中,有些数据结构原生具备Iterator接口(比如数组),即不用任何处理,就可以被` for...of ` 循环遍历,有些就不行(比如对象)。原因在于,这些数据结构原生部署了` Symbol.iterator ` 属性(详见下文),另外一些数据结构没有。凡是部署了` Symbol.iterator ` 属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
91
-
92
- 如果使用TypeScript的写法,遍历器接口(Iterable)、指针对象(Iterator)和next方法返回值的规格可以描述如下。
90
+ 如果使用 TypeScript 的写法,遍历器接口(Iterable)、指针对象(Iterator)和` next ` 方法返回值的规格可以描述如下。
93
91
94
92
``` javascript
95
93
interface Iterable {
@@ -106,13 +104,13 @@ interface IterationResult {
106
104
}
107
105
```
108
106
109
- ## 数据结构的默认Iterator接口
107
+ ## 默认 Iterator 接口
110
108
111
- Iterator接口的目的 ,就是为所有数据结构,提供了一种统一的访问机制,即` for...of ` 循环(详见下文)。当使用` for...of ` 循环遍历某种数据结构时,该循环会自动去寻找Iterator接口 。
109
+ Iterator 接口的目的 ,就是为所有数据结构,提供了一种统一的访问机制,即` for...of ` 循环(详见下文)。当使用` for...of ` 循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口 。
112
110
113
- 一种数据结构只要部署了Iterator接口 ,我们就称这种数据结构是”可遍历的“(iterable)。
111
+ 一种数据结构只要部署了 Iterator 接口 ,我们就称这种数据结构是”可遍历的“(iterable)。
114
112
115
- ES6规定,默认的Iterator接口部署在数据结构的 ` Symbol.iterator ` 属性,或者说,一个数据结构只要具有` Symbol.iterator ` 属性,就可以认为是“可遍历的”(iterable)。` Symbol.iterator ` 属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名` Symbol.iterator ` ,它是一个表达式,返回` Symbol ` 对象的` iterator ` 属性,这是一个预定义好的、类型为Symbol的特殊值 ,所以要放在方括号内。(参见Symbol一章 )。
113
+ ES6 规定,默认的 Iterator 接口部署在数据结构的 ` Symbol.iterator ` 属性,或者说,一个数据结构只要具有` Symbol.iterator ` 属性,就可以认为是“可遍历的”(iterable)。` Symbol.iterator ` 属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名` Symbol.iterator ` ,它是一个表达式,返回` Symbol ` 对象的` iterator ` 属性,这是一个预定义好的、类型为 Symbol 的特殊值 ,所以要放在方括号内(参见 Symbol 一章 )。
116
114
117
115
``` javascript
118
116
const obj = {
@@ -131,7 +129,18 @@ const obj = {
131
129
132
130
上面代码中,对象` obj ` 是可遍历的(iterable),因为具有` Symbol.iterator ` 属性。执行这个属性,会返回一个遍历器对象。该对象的根本特征就是具有` next ` 方法。每次调用` next ` 方法,都会返回一个代表当前成员的信息对象,具有` value ` 和` done ` 两个属性。
133
131
134
- 在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。
132
+ ES6 的有些数据结构原生具备 Iterator 接口(比如数组),即不用任何处理,就可以被` for...of ` 循环遍历。原因在于,这些数据结构原生部署了` Symbol.iterator ` 属性(详见下文),另外一些数据结构没有(比如对象)。凡是部署了` Symbol.iterator ` 属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
133
+
134
+ 原生具备 Iterator 接口的数据结构如下。
135
+
136
+ - Array
137
+ - Map
138
+ - Set
139
+ - String
140
+ - TypedArray
141
+ - 函数的 arguments 对象
142
+
143
+ 下面的例子是数组的` Symbol.iterator ` 属性。
135
144
136
145
``` javascript
137
146
let arr = [' a' , ' b' , ' c' ];
@@ -145,11 +154,11 @@ iter.next() // { value: undefined, done: true }
145
154
146
155
上面代码中,变量` arr ` 是一个数组,原生就具有遍历器接口,部署在` arr ` 的` Symbol.iterator ` 属性上面。所以,调用这个属性,就得到遍历器对象。
147
156
148
- 上面提到,原生就部署Iterator接口的数据结构有三类,对于这三类数据结构, 不用自己写遍历器生成函数,` for...of ` 循环会自动遍历它们。除此之外,其他数据结构(主要是对象)的Iterator接口 ,都需要自己在` Symbol.iterator ` 属性上面部署,这样才会被` for...of ` 循环遍历。
157
+ 对于原生部署 Iterator 接口的数据结构, 不用自己写遍历器生成函数,` for...of ` 循环会自动遍历它们。除此之外,其他数据结构(主要是对象)的 Iterator 接口 ,都需要自己在` Symbol.iterator ` 属性上面部署,这样才会被` for...of ` 循环遍历。
149
158
150
- 对象(Object)之所以没有默认部署Iterator接口 ,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口,就等于部署一种线性转换。不过,严格地说,对象部署遍历器接口并不是很必要,因为这时对象实际上被当作Map结构使用,ES5没有Map结构,而ES6原生提供了 。
159
+ 对象(Object)之所以没有默认部署 Iterator 接口 ,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口,就等于部署一种线性转换。不过,严格地说,对象部署遍历器接口并不是很必要,因为这时对象实际上被当作Map结构使用,ES5 没有 Map 结构,而 ES6 原生提供了 。
151
160
152
- 一个对象如果要有可被 ` for...of ` 循环调用的Iterator接口 ,就必须在` Symbol.iterator ` 的属性上部署遍历器生成方法(原型链上的对象具有该方法也可)。
161
+ 一个对象如果要具备可被 ` for...of ` 循环调用的 Iterator 接口 ,就必须在` Symbol.iterator ` 的属性上部署遍历器生成方法(原型链上的对象具有该方法也可)。
153
162
154
163
``` javascript
155
164
class RangeIterator {
@@ -175,11 +184,11 @@ function range(start, stop) {
175
184
}
176
185
177
186
for (var value of range (0 , 3 )) {
178
- console .log (value);
187
+ console .log (value); // 0, 1, 2
179
188
}
180
189
```
181
190
182
- 上面代码是一个类部署Iterator接口的写法 。` Symbol.iterator ` 属性对应一个函数,执行后返回当前对象的遍历器对象。
191
+ 上面代码是一个类部署 Iterator 接口的写法 。` Symbol.iterator ` 属性对应一个函数,执行后返回当前对象的遍历器对象。
183
192
184
193
下面是通过遍历器实现指针结构的例子。
185
194
@@ -190,24 +199,17 @@ function Obj(value) {
190
199
}
191
200
192
201
Obj .prototype [Symbol .iterator ] = function () {
193
- var iterator = {
194
- next: next
195
- };
202
+ var iterator = { next: next };
196
203
197
204
var current = this ;
198
205
199
206
function next () {
200
207
if (current) {
201
208
var value = current .value ;
202
209
current = current .next ;
203
- return {
204
- done: false ,
205
- value: value
206
- };
210
+ return { done: false , value: value };
207
211
} else {
208
- return {
209
- done: true
210
- };
212
+ return { done: true };
211
213
}
212
214
}
213
215
return iterator;
@@ -221,16 +223,13 @@ one.next = two;
221
223
two .next = three;
222
224
223
225
for (var i of one){
224
- console .log (i);
226
+ console .log (i); // 1, 2, 3
225
227
}
226
- // 1
227
- // 2
228
- // 3
229
228
```
230
229
231
230
上面代码首先在构造函数的原型链上部署` Symbol.iterator ` 方法,调用该方法会返回遍历器对象` iterator ` ,调用该对象的` next ` 方法,在返回一个值的同时,自动将内部指针移到下一个实例。
232
231
233
- 下面是另一个为对象添加Iterator接口的例子 。
232
+ 下面是另一个为对象添加 Iterator 接口的例子 。
234
233
235
234
``` javascript
236
235
let obj = {
@@ -254,7 +253,7 @@ let obj = {
254
253
};
255
254
```
256
255
257
- 对于类似数组的对象(存在数值键名和length属性),部署Iterator接口 ,有一个简便方法,就是` Symbol.iterator ` 方法直接引用数组的Iterator接口 。
256
+ 对于类似数组的对象(存在数值键名和 ` length ` 属性),部署 Iterator 接口 ,有一个简便方法,就是` Symbol.iterator ` 方法直接引用数组的 Iterator 接口 。
258
257
259
258
``` javascript
260
259
NodeList .prototype [Symbol .iterator ] = Array .prototype [Symbol .iterator ];
@@ -304,7 +303,7 @@ obj[Symbol.iterator] = () => 1;
304
303
[... obj] // TypeError: [] is not a function
305
304
```
306
305
307
- 上面代码中,变量obj的Symbol.iterator方法对应的不是遍历器生成函数 ,因此报错。
306
+ 上面代码中,变量 ` obj ` 的 ` Symbol.iterator ` 方法对应的不是遍历器生成函数 ,因此报错。
308
307
309
308
有了遍历器接口,数据结构就可以用` for...of ` 循环遍历(详见下文),也可以使用` while ` 循环遍历。
310
309
@@ -320,13 +319,13 @@ while (!$result.done) {
320
319
321
320
上面代码中,` ITERABLE ` 代表某种可遍历的数据结构,` $iterator ` 是它的遍历器对象。遍历器对象每次移动指针(` next ` 方法),都检查一下返回值的` done ` 属性,如果遍历还没结束,就移动遍历器对象的指针到下一步(` next ` 方法),不断循环。
322
321
323
- ## 调用Iterator接口的场合
322
+ ## 调用 Iterator 接口的场合
324
323
325
- 有一些场合会默认调用Iterator接口 (即` Symbol.iterator ` 方法),除了下文会介绍的` for...of ` 循环,还有几个别的场合。
324
+ 有一些场合会默认调用 Iterator 接口 (即` Symbol.iterator ` 方法),除了下文会介绍的` for...of ` 循环,还有几个别的场合。
326
325
327
326
** (1)解构赋值**
328
327
329
- 对数组和Set结构进行解构赋值时 ,会默认调用` Symbol.iterator ` 方法。
328
+ 对数组和 Set 结构进行解构赋值时 ,会默认调用` Symbol.iterator ` 方法。
330
329
331
330
``` javascript
332
331
let set = new Set ().add (' a' ).add (' b' ).add (' c' );
@@ -340,7 +339,7 @@ let [first, ...rest] = set;
340
339
341
340
** (2)扩展运算符**
342
341
343
- 扩展运算符(...)也会调用默认的iterator接口 。
342
+ 扩展运算符(...)也会调用默认的 Iterator 接口 。
344
343
345
344
``` javascript
346
345
// 例一
@@ -353,17 +352,17 @@ let arr = ['b', 'c'];
353
352
// ['a', 'b', 'c', 'd']
354
353
```
355
354
356
- 上面代码的扩展运算符内部就调用Iterator接口 。
355
+ 上面代码的扩展运算符内部就调用 Iterator 接口 。
357
356
358
- 实际上,这提供了一种简便机制,可以将任何部署了Iterator接口的数据结构 ,转为数组。也就是说,只要某个数据结构部署了Iterator接口 ,就可以对它使用扩展运算符,将其转为数组。
357
+ 实际上,这提供了一种简便机制,可以将任何部署了 Iterator 接口的数据结构 ,转为数组。也就是说,只要某个数据结构部署了 Iterator 接口 ,就可以对它使用扩展运算符,将其转为数组。
359
358
360
359
``` javascript
361
360
let arr = [... iterable];
362
361
```
363
362
364
363
** (3)yield* **
365
364
366
- yield* 后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
365
+ ` yield* ` 后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
367
366
368
367
``` javascript
369
368
let generator = function * () {
@@ -392,9 +391,9 @@ iterator.next() // { value: undefined, done: true }
392
391
- Promise.all()
393
392
- Promise.race()
394
393
395
- ## 字符串的Iterator接口
394
+ ## 字符串的 Iterator 接口
396
395
397
- 字符串是一个类似数组的对象,也原生具有Iterator接口 。
396
+ 字符串是一个类似数组的对象,也原生具有 Iterator 接口 。
398
397
399
398
``` javascript
400
399
var someString = " hi" ;
0 commit comments