一.Iterator (遍历器)的概念:
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只
要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是ES6创造了一种新的遍历命令for...of
循环,Iterator接口主要供for...of
消费。
1.在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构:
let arr = ['a','b','c'];
let it = arr[Symbol.iterator]();
iter.next(); // {value:'a',done:false}
iter.next(); // {value:'b',done:false}
iter.next(); // {value:'c',done:false}
iter.next(); // {value:undefined,done:true}
2.对象(Object)之所以没有默认部署Iterator接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需
要开发者手动指定。
本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口,就等于部署一种线性转换。
不过,严格地说,对象部署遍历器接口并不是很必要,因为这时对象实际上被当作Map结构使用,ES5没有Map结构,
而ES6原生提供了。
(1).一个对象如果要有可被for...of
循环调用的Iterator接口,就必须在Symbol.iterator
的属性上部署遍历器生成方
法(原型链上的对象具有该方法也可)。
class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
}
[Symbol.iterator]() { return this; }
next() {
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
} else {
return {done: true, value: undefined};
}
}
}
function range(start, stop) {
return new RangeIterator(start, stop);
}
for (var value of range(0, 3)) {
console.log(value); // 0 1 2
}
(2).代码首先在构造函数的原型链上部署Symbol.iterator
方法,调用该方法会返回遍历器对象iterator
,调用该对象
的next
方法,在返回一个值的同时,自动将内部指针移到下一个实例。
function Obj(value) {
this.value = value;
this.next = null;
}
Obj.prototype[Symbol.iterator] = function() {
var iterator = {
next: mynext
};
var current = this;
function mynext() {
if (current) {
var value = current.value;
current = current.next;
return {
done: false,
value: value
};
} else {
return {
done: true
};
}
}
return iterator;
}
var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);
one.next = two;
two.next = three;
for (var i of one){
console.log(i); // 1 2 3
}
(3).类似数组的对象调用数组的Symbol.iterator
方法的例子:
let iterable = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // 'a', 'b', 'c'
}
(4).普通对象部署数组的Symbol.iterator
方法,并无效果:
let iterable = {
a: 'a',
b: 'b',
c: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // undefined, undefined, undefined
}
二.for...of循环:
一个数据结构只要部署了Symbol.iterator
属性,就被视为具有iterator接口,就可以用for...of
循环遍历它的成员。
也就是说,for...of
循环内部调用的是数据结构的Symbol.iterator
方法。
for...of
循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments
对象、DOM NodeList 对象)、后文的 Generator对象,以及字符串。
1.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
});
for(let i of arr){
console.log(i); // red green blue
}
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
}
3.for...of
循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。这一点跟for...in
循环也不一样:
let arr = [3,5,7];
arr.hello = 'hello';
for(let i in arr){
console.log(i); // "0","1","2","3"
}
for(let i of arr){
console.log(i); // "3","5","7"
}