1、遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)
2、Iterator的作用有三个:一是为各种数据结构提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of使用
3、在ES6中,有些数据结构原生具备Iterator接口(比如数组),即不用任何处理,就可以被for...of循环遍历,有些就不行(比如对象)。原因在于,这些数据结构原生部署了Symbol.iterator属性,另外一些数据结构没有。凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象(ES6 规定,默认的 Iterator 接口部署在数据结构的 Symbol.iterator 属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator 属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。)
4、迭代过程如下:
(1)通过Symbol.iterator创建一个迭代器,指向当前数据结构的起始位置
(2)随后通过next方法进行向下迭代指向下一个位置,next方法会返回当前位置的对象,对象包含了value和done两个属性,value是当前属性的值,done用于判断遍历是否结束
(3)当done为true时则遍历结束
const items = ["zero", "one", "two"];
const it = items[Symbol.iterator]();
console.log(it.next()); // {value: "zero", done: false}
console.log(it.next()); // {value: "one", done: false}
console.log(it.next()); // {value: "two", done: false}
console.log(it.next()); // {value: undefined, done: true}
上面的例子首先创建一个数组,然后通过Symbol.iterator方法创建一个迭代器,之后不断的调用next方法对数组内部进行访问,当属性done为true时访问结束。迭代器是协议(使用它们的规则)的一部分,用于迭代。该协议额一个关键特性就是它是顺序的:迭代器一次返回一个值。这意味着如果可迭代数据结构是非线性的(例如树),迭代会使其线性化。
5、在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、String、Set和Map结构
(1)数组:数组 ( Array ) 和类型数组 ( TypedArray ) 他们是可迭代的
for (let item of ["zero", "one", "two"]) {
console.log(item); // zero one two
}
(2)String:字符串时可迭代的,但它们简历的是Unicode码,每个码包含一个到两个的JS字符
for (const c of 'z\uD83D\uDC0A') {
console.log(c); // z \uD83D\uDC0A
}
(3)Set:Set是对其元素进行迭代,迭代的顺序与其添加的顺序相同。WeakSets 不可迭代
const set = new Set();
set.add("zero");
set.add("one");
for (let item of set) {
console.log(item); // zero one
}
(4)Map:Map主要是迭代它们的 entries,每个 entry 都会被编码为 [key, value] 的项,entrie 是以确定的形式进行迭代,其顺序是与添加的顺序相同。WeakMaps 不可迭代
const map = new Map();
map.set(0, "zero");
map.set(1, "one");
for (let item of map) {
console.log(item); // [0, "zero"] [1, "one"]
}
(5)arguments:目前在ES6中使用越来越少,但是也可以遍历
function args() {
for (let item of arguments) {
console.log(item); // zero one
}
}
args("zero", "one");
6、以下场合会默认调用Iterator接口(即Symbol.iterator方法)
(1)for...of
(2)解构赋值
(3)扩展运算符(...)
(4)yield_yield后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口
(5)由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用
7、普通对象部署数组的Symbol.iterator方法,并无效果
8、字符串是一个类似数组的对象,也原生具有Iterator接口
9、遍历器对象除了具有next方法,还可以具有return方法和throw方法。如果你自己写遍历器对象生成函数,那么next方法是必须部署的,return方法和throw方法是否部署是可选的
10、Iterator的遍历过程实例与解析
class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
}
[Symbol.iterator](){
console.log('this:', this)
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
}
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
11、为对象添加Iterator
let obj = {
data: [ 'hello', 'world' ],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if(index < self.data.length){
return {
value: self.data[index++],
done: false
};
}else{
return {
value: undefined,
done: true
};
}
}
};
}
};
12、类数组对象调用数组的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'
}
网友评论