为什么我们需要迭代器?
var colours = ['red','green','yellow'];
for (var i = 0; i < colours.length; i ++) {
console.log(colours[i]);
}
以上代码是我们最为熟悉的一种循环语句了吧,我们通过一个变量i来跟踪数组的索引,但是这种代码有没有问题呢,如果是多个循环嵌套,我们需要使用多个跟踪变量,一不小心就会出现使用错误变量,导致程序出错的问题,那么迭代器的出现就是为了消除这种复杂性并减少循环中的错误。
什么是迭代器?
迭代器是一种特殊的对象,它具有一些专门为迭代过程设计的专有接口。让我们用ES5的语法创建一个迭代器。
function createIterator(items) {
var i = 0;
return {
next: function () {
var done = (i >= items.length);
var value = !done ? items[i++] : undefined;
return {
done: done,
value: value
}
}
}
}
var iterator = createIterator([1,2,3,4,5]);
从上面这段代码中,我们可以看出迭代器的特点:
- 迭代器对象有一个next()方法,每次调用next()方法都会返回出一个结果对象。
- 返回的结果对象有两个属性: done是一个布尔值表示是否完成迭代和value,表示下一个要返回的值
什么是生成器?
生成器是一种返回迭代器的函数。
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
// 生成器的调用方法与普通函数相同,只是它返回出一个迭代器
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
我们通过yield来制定调用迭代器的next()方法的返回值以及返回顺序
for-of循环
回到我们最开始给出的循环内部索引跟踪的相关问题,解决这个问题有两个方案:一个是迭代器,还有一个就是for-of循环。
for-of每执行一次都会调用可迭代对象的next()方法,并将迭代器返回的结果对象的value属性存储在一个变量中,循环将持续执行这一过程直到返回对象的done属性的值为true。
可迭代对象
在ES6中,所有的集合对象和字符串都是可迭代对象,这些对象中都有默认的迭代器,我们可以通过Symbol.iterator来访问对象默认的迭代器。
let values = [1,2,3,4,5];
let iterator = values[Symbol.iterator]();
console.log(iterator.next()); // {value:1, done:false}
创建可迭代对象
默认情况下,开发者定义的对象都是不可迭代对象,但是如果给Symbol.iterator属性添加一个生成器,则可以将其变成一个可迭代对象。
let collection = {
items: [1,2,3,4],
*[Symbol.iterator] () {
for (let item of this.items) {
yield item;
}
}
}
在这个实例中,先创建了一个生成器并将其赋值给对象的Symbol.iterator属性来创建默认的迭代器,而在生成器中,通过for-of循环迭代items数组并用yield返回每一个值。对象默认的迭代器返回值有属性items自动生成。
高级迭代器功能
(1)给迭代器传递参数
在之前的介绍中,我们知道迭代器有一些向外传值的方法,既可以用迭代器的next()方法返回值,也可以通过生成器yield关键字生成值。如果我们给迭代器的next()方法传递参数,则这个参数的值就会代替生成器内部上一条yield语句的返回值。
function *createIterator() {
let first = yield 1;
let second = yield first + 2;
yield second + 3;
}
let iterator = createIterator();
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next(3)); // {value: 5, done: false}
console.log(iterator.next(4)); // {value: 7, done: false}
当我们第一次调用next方法的时候无论传入什么参数都会被丢弃,因此传入的参数是用来替代上一次yield的返回值,而在第一次调用next方法之前,不会执行任何yield语句。
(2)生成器返回语句
function *createIterator() {
yield 4;
return 42;
}
let iterator = createIterator();
console.log(iterator.next()); // {value: 4, done: false}
console.log(iterator.next()); // {value: 42, done: true}
(3)委托生成器
function *createNumberIterator() {
yield 1;
yield 2;
}
function *createColorIterator() {
yield red;
yield blue;
}
function *createCombinedIterator() {
yield *createNumberIterator();
yield *createColorIterator();
yield true;
}
这里的生成器createCombinedIterator先后委托了另外的两个生成器。
异步任务执行
function run (taskDef) {
// 创建一个无使用限制的迭代器
let task = taskDef();
// 开始执行任务
let result = task.next();
// 循环调用next()函数
function step () {
if (!result.done()) {
result = task.next();
step();
}
}
step(); // 开始执行迭代
}







网友评论