迭代器
所有迭代器对象都有一个next()
方法,每次调用都返回一个结果对象。结果对象有两个属性:一个是value
,表示下一个将要返回的值;另一个是done
,它是一个布尔类型的值,当没有更多可返回数据时返回true
。
ES5中模拟一个迭代器:
function createIterator(items) {
var i = 0;
return {
next: function() {
var done = (i >= items.length);
var value = !done ? items[i++] : underfined;
return {
done: done,
value: value
};
}
};
}
var iterator = createIterator([1, 2, 3]);
console.log(iterator.next); // {value: 1, done: false}
console.log(iterator.next); // {value: 2, done: false}
console.log(iterator.next); // {value: 3, done: true}
console.log(iterator.next); // {value: underfined, done: true}
生成器
- 生成器是一种返回迭代器的函数,通过
function
关键字后的星号(*
)来表示,函数中会用到新的关键字yield
。 yield
关键字是ES6的新特性,可以通过它来指定调用迭代器的next()
方法时的返回值及返回顺序。每当执行完一条yield
语句后函数就会自动停止执行,直到再次调用迭代器的next()
方法才会继续执行下一个yield
。- 使用
yield
关键字可以返回任何值或表达式。
function* createIterator(items) {
for (let i = 0; i < items.length; i++) {
yield items[i];
}
}
// 生成器的调用方式与普通函数相同,只不过返回的是一个迭代器。
let iterator = createIterator([1, 2, 3]);
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 }
!!!注意:yield关键字只可在生成器内部使用,在其他地方使用会导致程序抛出语法错误。如:
function* createIterator(items) {
items.forEach(function(item) {
// 语法错误
yield item + 1;
})
}
可迭代对象(扩展)
可迭代对象具有Symbol.iterator
属性,是一种与迭代器密切相关的对象。Symbol.iterator
通过指定的函数可以返回一个作用于附属对象的迭代器。
由于生成器默认会为Symbol.iterator属性赋值,因此所有通过生成器创建的迭代器都是可迭代对象。在ES6中所有集合对象、字符串都是可迭代对象。
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属性添加一个生成器,则可以将其变为可迭代对象
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) {
console.log(x);
}
// 1
// 2
// 3
内建迭代器(扩展)
在ES6中对集合对象都内建了三种迭代器(数组、Set、Map)。
entries()
返回一个迭代器,其值为多个键值对。values()
返回一个迭代器,其值为集合的值。keys()
返回一个迭代器,其值为集合中的所有键名。
在for-of循环中,数组和Set默认是
values()
,Map默认是entries()
let colors = ['red', 'green', 'blue'];
for (let entry of colors.entries()) {
console.log(entry);
// [ 0, 'red' ] [ 1, 'green' ] [ 2, 'blue' ]
}
// 其余的就不一一举例了,有兴趣的自行体验
给迭代器传递参数(扩展)
如果给迭代器的next()
方法传递参数,则这个参数的值就会代替生成器内部上一条yield
语句的返回值。
第一次调用
next()
方法前不会执行任何yield语句,因此在第一次调用next()
方法时传递参数是毫无意义的。
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 }