let values = [1, 2, 3];
let iterator = values[Symbol.iterator]();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
由于具有Symbol.iterator属性的对象都有默认的迭代器,因此可以用它来检测对象是否为可迭代对象
function isIterable(object) {
return typeof object[Symbol.iterator] === "function";
}
console.log(isIterable([1, 2, 3])); // true
console.log(isIterable("Hello")); // true
console.log(isIterable(new Map())); // true
console.log(isIterable(new Set())); // true
console.log(isIterable(new WeakMap())); // false
console.log(isIterable(new WeakSet())); // false
使用Symbol.iterator来创建属于自己的迭代器
【创建可迭代对象】
默认情况下,开发者定义的对象都是不可迭代对象,但如果给Symbol.iterator属性添加一个生成器,则可以将其变为可迭代对象,讲collection修改为可迭代对象
在这个示例中,先创建一个生成器(注意,星号仍然在属性名前)并将其赋值给对象的Symbol.iterator属性来创建默认的迭代器;而在生成器中,通过for-of循环迭代this.items并用yield返回每一个值。collection对象默认迭代器的返回值由迭代器this.items自动生成,而非手动遍历来定义返回值
let collection = {
items: [],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
}
};
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);
for (let x of collection) {
//1
//2
//3
console.log(x);
}
迭代器是ES6的一个重要组成部分,在ES6中,已经默认为许多内建类型提供了内建迭代器,只有当这些内建迭代器无法实现目标时才需要自己创建。通常来说当定义自己的对象和类时才会遇到这种情况,否则,完全可以依靠内建的迭代器完成工作,而最常使用的可能是集合的那些迭代器
【集合对象迭代器】
在ES6中有3种类型的集合对象:数组、Map集合与Set集合
为了更好地访问对象中的内容,这3种对象都内建了以下三种迭代器
entries() 返回一个迭代器,其值为多个键值对 values() 返回一个迭代器,其值为集合的值 keys() 返回一个迭代器,其值为集合中的所有键名
迭代器的基础功能可以辅助完成很多任务,通过生成器创建迭代器的过程也很便捷,除了这些简单的集合遍历任务之外,迭代器也可以被用于完成一些复杂的任务
【给迭代器传递参数】
迭代器既可以用迭代器的next()方法返回值,也可以在生成器内部使用yield关键字来生成值。如果给迭代器的next()方法传递参数,则这个参数的值就会替代生成器内部上条yield语句的返回值。而如果要实现更多像异步编程这样的高级功能,那么这种给迭代器传值的能力就变得至关重要
function *createIterator() {
let first = yield 1;
let second = yield first + 2; // 4 + 2
yield second + 3; // 5 + 3
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.next(5)); // "{ value: 8, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
【在迭代器中抛出错误】
除了给迭代器传递数据外,还可以给它传递错误条件。通过throw()方法,当迭代器恢复执行时可令其抛出一个错误。这种主动抛出错误的能力对于异步编程而言至关重要,也能提供模拟结束函数执行的两种方法(返回值或抛出错误),从而增强生成器内部的编程弹性。将错误对象传给throw()方法后,在迭代器继续执行时其会被抛出
function *createIterator() {
let first = yield 1;
let second = yield first + 2; // yield 4 + 2 ,然后抛出错误
yield second + 3; // 永不会被执行
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.throw(new Error("Boom"))); // 从生成器中抛出了错误
在这个示例中,前两个表达式正常求值,而调用throw()方法后,在继续执行let second求值前,错误就会被抛出并阻止了代码继续执行。这个过程与直接抛出错误很相似,二者唯一的区别是抛出的时机不同
可以在生成器内部通过try-catch代码块来捕获这些错误
function *createIterator() {
let first = yield 1;
let second;
try {
second = yield first + 2; // yield 4 + 2 ,然后抛出错误
} catch (ex) {
second = 6; // 当出错时,给变量另外赋值
}
yield second + 3;
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.throw(new Error("Boom"))); // "{ value: 9, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
在此示例中,try-catch代码块包裹着第二条yield语句。尽管这条语句本身没有错误,但在给变量second赋值前还是会主动抛出错误,catch代码块捕获错误后将second变量赋值为6,下一条yield语句继续执行后返回9
这里有一个有趣的现象调用throw()方法后也会像调用next()方法一样返回一个结果对象。由于在生成器内部捕获了这个错误,因而会继续执行下一条yield语句,最终返回数值9
如此一来,next()和throw()就像是迭代器的两条指令,调用next()方法命令迭代器继续执行(可能提供一个值),调用throw()方法也会命令迭代器继续执行,但同时也抛出一个错误,在此之后的执行过程取决于生成器内部的代码
在迭代器内部,如果使用了yield语句,则可以通过next()方法和throw()方法控制执行过程,当然,也可以使用return语句返回一些与普通函数返回语句不太一样的内容