@@ -41,7 +41,7 @@ it.next() // { value: undefined, done: true }
4141
4242function 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
8686Iterator接口的开发目的,就是为所有数据结构,提供了一种统一的访问机制,即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
114114class MySpecialTree {
115115 // ...
116- [Symbol .iterator ]() {
116+ [Symbol .iterator ]() {
117117 // ...
118118 return theIterator;
119119 }
@@ -131,11 +131,11 @@ function Obj(value){
131131}
132132
133133Obj .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
234248let set = new Set ().add (' a' ).add (' b' ).add (' c' );
235-
249+
236250let [x,y] = set;
237251// x='a'; y='b'
238-
252+
239253let [first, ... rest] = set;
240254// first='a'; rest=['b','c'];
241255
@@ -286,7 +300,7 @@ let arr = [...iterable];
286300var arr = [1 , 5 , 7 ];
287301var arrEntries = arr .entries ();
288302
289- arrEntries .toString ()
303+ arrEntries .toString ()
290304// "[object Array Iterator]"
291305
292306arrEntries === arrEntries[Symbol .iterator ]()
@@ -301,7 +315,7 @@ arrEntries === arrEntries[Symbol.iterator]()
301315``` javascript
302316
303317var someString = " hi" ;
304- typeof someString[Symbol .iterator ]
318+ typeof someString[Symbol .iterator ]
305319// "function"
306320
307321var iterator = someString[Symbol .iterator ]();
@@ -323,7 +337,7 @@ var str = new String("hi");
323337[... str] // ["h", "i"]
324338
325339str[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"]
340354str // "hi"
341355
342356```
@@ -352,9 +366,9 @@ str // "hi"
352366var myIterable = {};
353367
354368myIterable[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
382404for...of循环可以使用的范围包括数组、Set和Map结构、某些类似数组的对象(比如arguments对象、DOM NodeList对象)、后文的Generator对象,以及字符串。
383405
@@ -391,11 +413,11 @@ const arr = ['red', 'green', 'blue'];
391413let iterator = arr[Symbol .iterator ]();
392414
393415for (let v of arr) {
394- console .log (v); // red green blue
416+ console .log (v); // red green blue
395417}
396418
397419for (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') {
546573let 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+
0 commit comments