ES6 —(Iterator 和 for...of)
2017-08-29 08:50
375 查看
1、Iterator(遍历器)的概念
JavaScript 共有四种数据集合:数组Array、对象Object、Map、Set。后两个是 ES6 添加的。Iterator 是一种接口机制,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署了 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:
一是为各种数据结构提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是 ES6 创造了一种新的遍历命令
for...of循环,Iterator 接口主要供
for...of消费。
Iterator 的遍历过程如下:
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的 next 方法,可以将指针对象指向数据结构的第一个成员。
(3)第二次调用指针对象的 next 方法,指针对象就指向数据结构的第二个成员。
(4)不断调用指针对象的 next 方法,直到指针对象指向数据结构的结束位置。
每一次调用 next 方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含 value 和 done 两个属性的对象。其中 value 属性是当前成员的值,done 属性是一个布尔值,表示遍历是否结束。
2、默认 Iterator 接口
当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(Iterable)。
ES6 规定,默认的 Iterator 接口部署在数据结构的
Symbol.iterator属性上。或者说,一个数据结构只要具有
Symbol.iterator属性,就可以认为是可遍历的。
Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名
Symbol.iterator它是一个表达式,返回Symbol 对象的 iterator 属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内。
ES6 的有些数据结构具备 Iterator 接口,即不用任何处理,就可以被
for...of循环遍历。凡是部署了
Symbol.iterator属性的数据结构,就被称为部署了变量器接口。调用这个接口,就会返回一个遍历器对象。
原生具备 Iterator 接口的数据结构如下:
Array
Map
Set
String
TypedArray
函数的 arguments 对象
NodeList 对象
let arr = ['a','b','c']; let iter = arr[Symbol.iterator](); console.log(iter.next()); // Object {value: "a", done: false} console.log(iter.next()); // Object {value: "b", done: false} console.log(iter.next()); // Object {value: "c", done: false} console.log(iter.next()); // Object {value: undefined, done: true}
不具备 Iterator 接口的数据结构(主要是对象),都需要自己在
Symbol.iterator属性上面部署遍历器生成方法,这样才会被
for...of循环遍历。例如下面通过遍历器实现指针结构的例子。
function Obj(value){ this.value = value; this.next = null; } Obj.prototype[Symbol.iterator] = function(){ var iterator = {next: next}; // 第一个为属性名,第二个为下面的 next() 方法 var current = this; function next(){ if(current){ var value = current.value; current = current.next; return {done: false, value: value}; } else { return {done: true, value: undefined}; } } return iterator; } var one = new Obj(1); var two = new Obj(2); var three = new Obj(3); one.next = two; two.next = three; for(let i of one){ console.log(i); }
上述代码,首先在构造函数的原型链上部署
Symbol.iterator方法,调用该方法会返回遍历器对象 iterator ,调用该对象的 next 方法,在返回一个值的同时,自动将内部指针移到下一个实例。
注意:如果
Symbol.iterator属性对象的不是一个遍历器生成函数(即会返回一个遍历器对象)解释引擎会报错。
3、调用 Iterator 接口的场合
有一些场合会默认调用 Iterator 接口(即Symbol.iterator方法),如下:
(1)解构赋值
对数组和 Set 结构进行解构赋值时,会默认调用
Symbol.iterator方法。
(2)扩展运算符
扩展运算符(
...)也会调用默认的 Iterator 接口。
(3)yield*
yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
(4)for…of
for...of也会调用默认的 Iterator 接口。
(5)其他场合
由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其他都调用了遍历器接口,如:
Array.form()
Map(), Set(), WeakMap(), WeakMap()(比如: new Map([[‘a’, 1], [‘b’, 2]]))
Promise.all()
Promise.race()
4、字符串的 Iterator 接口
字符串是一个类似数组的对象,也原生具有 Iterator 接口。var str = "hi"; console.log(typeof str[Symbol.iterator]); // function var ite = str[Symbol.iterator](); console.log(ite.next()); // Object {value: "h", done: false} console.log(ite.next()); // Object {value: "i", done: false} console.log(ite.next()); // Object {value: undefined, done: true}
5、遍历器对象的 return(),throw()
遍历器对象除了具有 next 方法,还可以具有 return() 方法和 throw() 方法。如果自己写遍历器对象的生成器函数,那么 next 方法是必须部署的, return 和 throw 方法是否部署是可选的。return 方法的使用场合是,如果 for…of 循环提前退出(通常是因为出错,或者有 break 语句或 continue 语句),就会调用 return 方法。如果一个对象在遍历前,需要清理或释放资源,就可以部署 return 方法。 如
for(let i of arr){ break; } for(let i of arr){ continue; } for(let i of arr){ throw new Error(); }
注意:return 方法必须返回一个对象,这是 Generator 规格决定的。
throw方法主要是配合 Generator 函数使用,一般的遍历器对象用不到这个方法。
6、for…of 循环
ES6 引入for...of循环,作为遍历所有数据结构的统一方法。
一个数据结构只要部署了
Symbol.iterator属性,就被视为具有 iterator 接口,就可以用
for...of循环遍历成员,即
for...of循环内部调用的是数据结构的
Symbol.iterator方法。
for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(如 arguments 对象、DOM NodeList 对象)、Generator 对象,以及字符串。
(1)数组
数组原生具备 Iterator 接口(即默认部署了 Symbol.iterator 属性)。
for...of循环可以替代数组实例的
forEach方法。
for...in循环,只能获得对象的键名,不能直接获取键值,而
for...of循环,允许遍历获得键值。并且,
for...of循环调用遍历接口,数组的遍历器只返回具有数字索引的属性,而
for...in能返回非数字键名。
(2)Set 和 Map 结构
Set 和 Map 结构原生具备 Iterator 接口(即默认部署了 Symbol.iterator 属性),可以直接使用
for...of循环。
注意:首先,遍历的的顺序是按照各个成员被添加进数据结构的顺序。其次,Set 结构遍历时,返回的是一个值,而 Map 结构遍历时,返回的是一个数组,该数组的两个成员分别是当前 Map 成员的键名和键值。
(3)计算生成的数据结构
调用 entries()、 keys()、 values() 方法生成的遍历器对象。
for...of所遍历的都是计算生成的数据结构。
(4)类似数组的对象
类似数组的对象包括好几类,如 字符串、DOM NodeList 对象、arguments 对象等。
注意:
for...of循环在遍历字符串是能正确识别 32 位的 UTF-16 字符。如果类似数组的对象没有 Iterator 接口,可以使用 Array.from 方法将其转为数组。
(5)对象
对于普通的对象,
for...of结构不能直接使用,会报错,必须部署了 Iterator 接口后才能使用。但是,这样情况下,
for...in循环依然可以用来遍历键名。
一种解决方法是,使用 Object.keys 方法将对象的键名生成一个数组,然后遍历这个数组。
for(var key of Object.keys(obj)){ console.log(`${key}: ${obj[key]}`); }
另一种方法是使用 Generator 函数将对象重新包装一下。
function* entries(obj){ for(let key of Object.keys(obj)){ yield [key, obj[key]]; } } for(let [key, value] of entries(obj)){ console.log(key, value); }
阮一峰:ECMAScript 6入门
相关文章推荐
- ES6入门之Iterator和for...of
- ES6(Iterator 和 for...of 循环)
- ES6中Iterator与for..of..遍历用法分析
- 【es6】Iterator 和 for...of
- ES6入门——Iterator和for...of循环
- es6入门(五)Iterator以及for ...of
- ES6系列之---Iterator和for...of循环
- es6-Iterator与for...of
- es6 javascript的Iterator 和 for...of 循环
- ES6--iterator和for-of循环
- ES6--Iterator和for...of循环
- ES6---for...of遍历‘类似’数组的数据结构,array set map string与iterator
- es6 iterator和for...of循环
- ES6 之 Set数据结构和Map数据结构 Iterator和for...of循环
- ES6新特性二:Iterator(遍历器)和for-of循环详解
- ES6_Iterator 和 for...of 循环
- ES6 迭代器(Iterator)和 for.of循环使用方法学习(总结)
- ES6 -- 遍历器Iterator和for...of
- ES6-Iterator与for..of..遍历
- ES6(二) Iterator(遍历器)和for-of循环