目录
迭代器
迭代器/遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署
Iterator
接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是ES6创造了一种新的遍历命令
for...of
循环,Iterator
接口主要供for...of
消费。
Iterator 的遍历过程是这样的:
创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
不断调用指针对象的next方法,直到它指向数据结构的结束位置。
迭代器实现
Iterator
接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of
循环(详见下文)。当使用for...of
循环遍历某种数据结构时,该循环会自动去寻找Iterator
接口。一种数据结构只要部署了Iterator
接口,我们就称这种数据结构是“可遍历的”(iterable)。下面的例子是数组的
Symbol.iterator
属性。let arr = ['a', 'b', 'c']; // 获取迭代器对象 let iter = 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 }
原生具备 Iterator 接口的数据结构如下:Array、Map、Set、String、TypedArray、arguments、NodeList 等
迭代器协议
迭代器协议定义了产生一系列值(无论是有限个还是无限个)的标准方式。当值为有限个时,所有的值都被迭代完毕后,则会返回一个默认返回值。
只有实现了一个拥有以下语义(semantic)的 next() 方法,一个对象才能成为迭代器:
| 属性 | 值 | | :-----:| ---- | ---- | | next | 一个无参数函数,返回一个应当拥有以下两个属性的对象:done(boolean)如果迭代器可以产生序列中的下一个值,则为 false。(这等价于没有指定 done 这个属性。)如果迭代器已将序列迭代完毕,则为 true。这种情况下,value 是可选的,如果它依然存在,即为迭代结束之后的默认返回值。value迭代器返回的任何 JavaScript 值。done 为 true 时可省略。next() 方法必须返回一个对象,该对象应当有两个属性: done 和 value,如果返回了一个非对象值(比如 false 或 undefined),则会抛出一个 TypeError 异常("iterator.next() returned a non-object value")。|
遍历
遍历上方someString使用如下方法:
方法一:使用迭代器的
next
方法遍历someString的迭代器对象let someString = "hi"; // 获取遍历器对象 let iterator = someString[Symbol.iterator](); let result; // 使用next遍历迭代器对象 while (!(result = iterator.next()).done) { console.log(result); }
方法二:因为someString是字符串,本身部署了
Iterator
接口,可以使用for-of
遍历someStringlet someString = "hi"; for (let key of someString) { console.log(key); }
扩展
(授课不作要求):
数组、Map等结构中的成员都是有顺序的,即都是线性的结构,而对象,各成员并没有一个确定的顺序,所以遍历时先遍历谁后遍历谁并不确定。所以,给一个对象部署iterator接口,其实就是对该对象做一种线性转换。如果你有这种需要,就需要手动给你的对象部署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 }; } } }; } };
当然,不知道你们看到next是否想到了es6的一个新玩意儿,即Generator函数。用Generator函数来实现Symbol.iterator接口,事半功倍。
var yieldIterator = {}; yieldIterator[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; [...yieldIterator] // [1, 2, 3]