1.原生Iterator(以数组为例)
原生默认具备Iterator接口的数据结构如下:
- Array
- Set
- Map
- String
- arguments对象
- NodeList对象
对于这些数据,我们可以直接对其进行遍历。以一个数组为例:
let arr = ['aaa', 'bbb', 'ccc', 'ddd'];
直接用for of遍历
for (const item of arr) {
console.log(item);
} // 依次输出aaa bbb ccc ddd
获取迭代器,并用next()遍历
// 获取迭代器
let iter = arr[Symbol.iterator]();
// 返回的是迭代器对象
console.log(iter); // Array Iterator
// 用next()遍历
console.log(iter.next()); // {value: 'aaa', done: false}
console.log(iter.next()); // {value: 'bbb', done: false}
console.log(iter.next()); // {value: 'ccc', done: false}
console.log(iter.next()); // {value: 'ddd', done: false}
console.log(iter.next()); // {value: undefined, done: true}
next()返回该数据结构的当前成员的信息,value表示该成员的值,done表示遍历是否结束。
2. 不具备Iterator的对象
如果没有内置的迭代器接口,就从其他地方“偷”过来。但前提是,被迭代的对象必须具有线性的结构,即谁先谁后,必须清楚。(否则怎么知道成员的迭代顺序呢?)在这里,用一个普通对象来模拟一个数组。
let obj = {
0: 'aaa',
1: 'bbb',
2: 'ccc',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator] // 偷了
}
对象本身是非线性的,但给它增加了名为0,1,2的属性,因此具备了线性的结构。再加上length属性和从Array原型那里偷来的迭代器接口,这个对象就可以伪装成一个数组了。
现在,可以直接用for of遍历了。
for (const item of obj) {
console.log(item);
} // 依次打印aaa bbb ccc
3.Iterator的应用场景:防止误改数据
let obj2 = {
code: 200,
name: 'obj2',
list: ['aaa', 'bbb', 'ccc'],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
// return {
// value: this.list[index++],
// done: index >= (this.list.length + 1) ? true : false
// };
// 下面这个更易懂
return index < this.list.length
// ? { value: this.list[index++], done: false }
// : { value: undefined, done: true }
// 对于迭代器对象来说,done:false和value:undefined属性都是可以省略的
// 因此可以写为:
? { value: this.list[index++] } :
: { done: true };
}
}
}
}
多数情况,我们往往只关心obj2这个对象里面存储的数组list是什么,并不愿修改它;其他属性如code、name(这里只是随便取名)相比之下并不常用,但又不可删除。
因此我们希望能增添一个保护机制,防止手抖误将数据修改。让数据只可读、不可写。
假设在类创建时,我们已经将code、name、list作为私有属性,这里不再演示。这样的话,就不可以通过obj2.list这种方式访问或修改数据,那么迭代器的作用就显现出来了。在上述代码中,我们已经给obj2部署了Iterator,它现在已经具有了可迭代性。
直接用for of遍历
for (const item of obj2) {
console.log(item);
} // 依次输出aaa bbb ccc
获取迭代器,并用next()遍历
let iter2 = obj2[Symbol.iterator]();
console.log(iter2); // {next: ƒ}
console.log(iter2.next()); // {value: 'aaa'}
console.log(iter2.next()); // {value: 'bbb'}
console.log(iter2.next()); // {value: 'ccc'}
console.log(iter2.next()); // {done: true}
这里打印输出迭代器对象iter2时,直接得到了手写的next()方法。
而在第一节中,打印输出数组的原生迭代器对象iter时,得到的是Array Iterator,其原型中包含正宗的next()方法,如下图所示。
使用扩展运算符和Array.from()构建成数组
console.log([...obj2]); // ['aaa', 'bbb', 'ccc']
console.log(Array.from(obj2)); // ['aaa', 'bbb', 'ccc']