一、 概念
JavaScript原有表示“集合”的数据结构,主要是数组(' Array ')和对象(' Object ' ),ES6又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理不同的数据结构。
遍历器(Iterator)就是这样一种机制。它是一种接口,为不同的数据结构提供一种访问机制,即for ... of 循环。当使用
for...of
循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
二、本质
**迭代器对象本质上,就是一个指针对象。通过指针对象的next(), 用来移动指针。
****迭代器协议:对象必须提供一个next(),执行该方法要么返回迭代的下一项,要么就引起Stopiteration异常,以终止迭代。**
每调用一次next ()方法,都会返回一个对象,都会返回数据结构的当前成员的信息。这个对象有 value 和 done 两个属性,value属性返回当前位置的成员,done属性是一个布尔值,表示遍历是否结束,即是否有必要再调用一次next () 。对于遍历器来说,value:undefined和done:false属性都是可以省略的。
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性上;或者说,一个数据结构只要有Symbol.iterator属性,就认为是可遍历的。
三、实现Iterator接口的原生对象
原生具备Iterator接口的数据结构有:
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
可以看到Array原型对象已经实现了Iterator这个属性:
那么数组的实例对象也拥有这个属性,可以调用试试:
下面是模拟next()方法返回的例子:
上面的代码定义了一个makeIterator函数,它是一个遍历器生成的函数,作用就是返回一个遍历器对象。**对数组[ ’a', 'b' ]执行这个函数,就会返回该数组的遍历器对象(指针对象)it**。指针对象的
next
方法,用来移动指针。开始时,指针指向数组的开始位置。然后,每次调用
next
方法,指针就会指向数组的下一个成员。第一次调用,指向
a
;第二次调用,指向
b
。
** 总之,调用指针对象的next()方法,就可以遍历事先给定的数据结构。**
上面说了,对于遍历器对象来说, done:false 和 value:undefined属性都是可以省略的,因此上面的
makeIterator
函数可以简写成下面的形式。
** 使用场合:**
① 对实现了Iterator接口的数据解构赋值
② 扩展运算符
上面代码的扩展运算符内部就调用 Iterator 接口。
实际上,这提供了一种简便机制,可以将任何部署了 Iterator 接口的数据结构,转为数组。也就是说,只要某个数据结构部署了 Iterator 接口,就可以对它使用扩展运算符,将其转为数组。
三、 for... of 循环
for...of
循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如
arguments
对象、DOM NodeList 对象)、字符串等。
数组原生具备iterator接口,(默认部署Symbol.iterator属性),for...of 循环本质上就是调用这个接口产生的遍历器:
const arr = ['red', 'green', 'blue'];
for(let v of arr) {
console.log(v); // red green blue
}
const obj = {};
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);
for(let v of obj) {
console.log(v); // red green blue
}
上面代码中,空对象
obj
部署了数组
arr
的
Symbol.iterator
属性,结果
obj
的
for...of
循环,产生了与
arr
完全一样的结果。
for ... of 循环可以代替数组实例 forEach 方法。
const arr = ['red', 'green', 'blue'];
arr.forEach(function (element, index) {
console.log(element); // red green blue
console.log(index); // 0 1 2
});
JavaScript 原有的
for...in
循环,只能获得对象的键名,不能直接获取键值。ES6 提供
for...of
循环,允许遍历获得键值。
var arr = ['a', 'b', 'c', 'd'];
for (let a in arr) {
console.log(a); // 0 1 2 3
}
for (let a of arr) {
console.log(a); // a b c d
}
四、 Set 和 Map结构
Set和Map结构也原生具有Interator接口,可以直接使用for...of循环。
var engines = new Set(['1','2','3']);
for (var e of engines) {
console.log(e); // 1 2 3
}
var class = new Map();
class .set('Tom', 12);
class .set('Lala', 13);
class .set('9300', 14);
for (var [name,value] of class) {
console.log(name + ':' + number); // Tom:12 Lala:13 9300:14
}
以上代码演示了如何遍历Set和Map结构。需要注意的是,首先遍历的顺序是按照哥哥成员被添加进数据结构的顺序。其次,Set结构遍历时,返回的是一个值。而Map结构遍历时,返回的是一个数组,该数组的两个成员分别是当前Map成员的键名和键值。
let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
console.log(pair);
}
// ['a', 1]
// ['b', 2]
for (let [key, value] of map) {
console.log(key + ' : ' + value);
}
// a : 1
// b : 2
其他内容可参照 ES6入门
版权归原作者 93 Million Miles- 所有, 如有侵权,请联系我们删除。