Skip to content

Commit e39ea78

Browse files
committed
edit iterator
1 parent 13df0a4 commit e39ea78

File tree

2 files changed

+113
-21
lines changed

2 files changed

+113
-21
lines changed

docs/iterator.md

Lines changed: 112 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ it.next() // { value: undefined, done: true }
4141

4242
function idMaker(){
4343
var index = 0;
44-
44+
4545
return {
4646
next: function(){
4747
return {value: index++, done: false};
@@ -85,7 +85,7 @@ interface IterationResult {
8585

8686
Iterator接口的开发目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环(见后文的介绍)。当使用for...of循环,遍历某种数据结构时,该循环会自动去寻找Iterator接口。
8787

88-
ES6规定,默认的Iterator接口部署在数据结构的Symbol.iterator属性。也就是说,调用Symbol.iterator方法,就会得到当前数据结构的默认遍历器。Symbol.iterator是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为Symbol的特殊值,所以要放在方括号内(请参考Symbol一节)。
88+
ES6规定,默认的Iterator接口部署在数据结构的Symbol.iterator属性,或者一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。也就是说,调用Symbol.iterator方法,就会得到当前数据结构的默认遍历器。Symbol.iterator是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为Symbol的特殊值,所以要放在方括号内(请参考Symbol一节)。
8989

9090
在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。
9191

@@ -113,7 +113,7 @@ iter.next() // { value: undefined, done: true }
113113

114114
class MySpecialTree {
115115
// ...
116-
[Symbol.iterator]() {
116+
[Symbol.iterator]() {
117117
// ...
118118
return theIterator;
119119
}
@@ -131,11 +131,11 @@ function Obj(value){
131131
}
132132

133133
Obj.prototype[Symbol.iterator] = function(){
134-
134+
135135
var iterator = {
136136
next: next
137137
};
138-
138+
139139
var current = this;
140140

141141
function next(){
@@ -221,6 +221,20 @@ obj[Symbol.iterator] = () => 1;
221221

222222
上面代码中,变量obj的Symbol.iterator方法返回的不是遍历器,因此报错。
223223

224+
有了遍历器接口,数据结构就可以用for...of循环遍历(详见下文),也可以使用while循环遍历。
225+
226+
```javascript
227+
var $iterator = ITERABLE[Symbol.iterator]();
228+
var $result = $iterator.next();
229+
while (!$result.done) {
230+
var x = $result.value;
231+
// ...
232+
$result = $iterator.next();
233+
}
234+
```
235+
236+
上面代码中,ITERABLE代表某种可遍历的数据结构,$iterator是它的遍历器。遍历器每次移动指针(next方法),都检查一下返回值的done属性,如果遍历还没结束,就移动遍历器的指针到下一步(next方法),不断循环。
237+
224238
### 调用默认iterator接口的场合
225239

226240
有一些场合会默认调用iterator接口(即Symbol.iterator方法),除了下文会介绍的for...of循环,还有几个别的场合。
@@ -232,10 +246,10 @@ obj[Symbol.iterator] = () => 1;
232246
```javascript
233247

234248
let set = new Set().add('a').add('b').add('c');
235-
249+
236250
let [x,y] = set;
237251
// x='a'; y='b'
238-
252+
239253
let [first, ...rest] = set;
240254
// first='a'; rest=['b','c'];
241255

@@ -286,7 +300,7 @@ let arr = [...iterable];
286300
var arr = [1, 5, 7];
287301
var arrEntries = arr.entries();
288302

289-
arrEntries.toString()
303+
arrEntries.toString()
290304
// "[object Array Iterator]"
291305

292306
arrEntries === arrEntries[Symbol.iterator]()
@@ -301,7 +315,7 @@ arrEntries === arrEntries[Symbol.iterator]()
301315
```javascript
302316

303317
var someString = "hi";
304-
typeof someString[Symbol.iterator]
318+
typeof someString[Symbol.iterator]
305319
// "function"
306320

307321
var iterator = someString[Symbol.iterator]();
@@ -323,7 +337,7 @@ var str = new String("hi");
323337
[...str] // ["h", "i"]
324338

325339
str[Symbol.iterator] = function() {
326-
return {
340+
return {
327341
next: function() {
328342
if (this._first) {
329343
this._first = false;
@@ -336,7 +350,7 @@ str[Symbol.iterator] = function() {
336350
};
337351
};
338352

339-
[...str] // ["bye"]
353+
[...str] // ["bye"]
340354
str // "hi"
341355

342356
```
@@ -352,9 +366,9 @@ str // "hi"
352366
var myIterable = {};
353367

354368
myIterable[Symbol.iterator] = function* () {
355-
yield 1;
356-
yield 2;
357-
yield 3;
369+
yield 1;
370+
yield 2;
371+
yield 3;
358372
};
359373
[...myIterable] // [1, 2, 3]
360374

@@ -375,9 +389,17 @@ for (let x of obj) {
375389

376390
```
377391

392+
### return(),throw()
393+
394+
遍历器除了具有next方法(必备),还可以具有return方法和throw方法(可选)。
395+
396+
for...of循环如果提前退出(通常是因为出错,或者有break语句或continue语句),就会调用return方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法。
397+
398+
throw方法主要是配合Generator函数使用,一般的遍历器用不到这个方法。请参阅《Generator函数》的章节。
399+
378400
## for...of循环
379401

380-
ES6中,一个数据结构只要部署了Symbol.iterator方法,就被视为具有iterator接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法
402+
ES6借鉴C++、Java、C#和Python语言,引入了for...of循环,作为遍历所有数据结构的统一的方法。一个数据结构只要部署了`Symbol.iterator`方法,就被视为具有iterator接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的`Symbol.iterator`方法
381403

382404
for...of循环可以使用的范围包括数组、Set和Map结构、某些类似数组的对象(比如arguments对象、DOM NodeList对象)、后文的Generator对象,以及字符串。
383405

@@ -391,11 +413,11 @@ const arr = ['red', 'green', 'blue'];
391413
let iterator = arr[Symbol.iterator]();
392414

393415
for(let v of arr) {
394-
console.log(v); // red green blue
416+
console.log(v); // red green blue
395417
}
396418

397419
for(let v of iterator) {
398-
console.log(v); // red green blue
420+
console.log(v); // red green blue
399421
}
400422

401423
```
@@ -471,11 +493,16 @@ for (let pair of map) {
471493
// ['a', 1]
472494
// ['b', 2]
473495

496+
for (let [key, value] of map) {
497+
console.log(key + ' : ' + value);
498+
}
499+
// a : 1
500+
// b : 2
474501
```
475502

476503
### 计算生成的数据结构
477504

478-
ES6的数组、Set、Map都部署了以下三个方法,调用后都返回遍历器。
505+
有些数据结构是在现有数据结构的基础上,计算生成的。比如,ES6的数组、Set、Map都部署了以下三个方法,调用后都返回遍历器。
479506

480507
- entries() 返回一个遍历器,用来遍历 [键名, 键值] 组成的数组。对于数组,键名就是索引值;对于Set,键名与键值相同。Map结构的iterator接口,默认就是调用entries方法。
481508
- keys() 返回一个遍历器,用来遍历所有的键名。
@@ -546,12 +573,12 @@ for (let x of 'a\uD83D\uDC0A') {
546573
let arrayLike = { length: 2, 0: 'a', 1: 'b' };
547574

548575
// 报错
549-
for (let x of arrayLike) {
576+
for (let x of arrayLike) {
550577
console.log(x);
551578
}
552579

553580
// 正确
554-
for (let x of Array.from(arrayLike)) {
581+
for (let x of Array.from(arrayLike)) {
555582
console.log(x);
556583
}
557584

@@ -585,4 +612,68 @@ for (e of es6) {
585612

586613
上面代码表示,对于普通的对象,for...in循环可以遍历键名,for...of循环会报错。
587614

588-
在对象上部署iterator接口的代码,参见本章前面部分。
615+
一种解决方法是,使用`Object.keys`方法将对象的键名生成一个数组,然后遍历这个数组。
616+
617+
```javascript
618+
for (var key of Object.keys(someObject)) {
619+
console.log(key + ": " + someObject[key]);
620+
}
621+
```
622+
623+
在对象上部署iterator接口的代码,参见本章前面部分。一个方便的方法是将数组的`Symbol.iterator`属性,直接赋值给其他对象的`Symbol.iterator`属性。比如,想要让for...of循环遍历jQuery对象,只要加上下面这一行就可以了。
624+
625+
```javascript
626+
jQuery.prototype[Symbol.iterator] =
627+
Array.prototype[Symbol.iterator];
628+
```
629+
630+
### 与其他遍历语法的比较
631+
632+
以数组为例,JavaScript提供多种遍历语法。最原始的写法就是for循环。
633+
634+
```javascript
635+
for (var index = 0; index < myArray.length; index++) {
636+
console.log(myArray[index]);
637+
}
638+
```
639+
640+
这种写法比较麻烦,因此数组提供内置的forEach方法。
641+
642+
```javascript
643+
myArray.forEach(function (value) {
644+
console.log(value);
645+
});
646+
```
647+
648+
这种写法的问题在于,无法中途跳出forEach循环,break命令或return命令都不能奏效。
649+
650+
for...in循环可以遍历数组的键名。
651+
652+
```javascript
653+
for (var index in myArray) {
654+
console.log(myArray[index]);
655+
}
656+
```
657+
658+
for...in循环有几个缺点。
659+
660+
1)数组的键名是数字,但是for...in循环是以字符串作为键名“0”、“1”、“2”等等。
661+
662+
2)for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
663+
664+
3)某些情况下,for...in循环会以任意顺序遍历键名。
665+
666+
总之,for...in循环主要是为遍历对象而设计的,不适用于遍历数组。
667+
668+
for...of循环相比上面几种做法,有一些显著的优点。
669+
670+
```javascript
671+
for (let value of myArray) {
672+
console.log(value);
673+
}
674+
```
675+
676+
- 有着同for...in一样的简洁语法,但是没有for...in那些缺点。
677+
- 不同用于forEach方法,它可以与break、continue和return配合使用。
678+
- 提供了遍历所有数据结构的统一操作接口。
679+

docs/reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
- Harold Cooper, [Coroutine Event Loops in Javascript](http://syzygy.st/javascript-coroutines/): Generator用于实现状态机
7777
- Ruslan Ismagilov, [learn-generators](https://github.com/isRuslan/learn-generators): 编程练习,共6道题
7878
- Kyle Simpson, [Iterating ES6 Numbers](http://blog.getify.com/iterating-es6-numbers/): 在数值对象上部署遍历器
79+
- Steven Sanderson, [Experiments with Koa and JavaScript Generators](http://blog.stevensanderson.com/2013/12/21/experiments-with-koa-and-javascript-generators/): Generator入门介绍,以Koa框架为例
7980

8081
## Promise对象
8182

0 commit comments

Comments
 (0)