一、迭代器的特征
迭代器有一个next()方法,每次调用时会返回一个对象,该对象的结构为{value:xxx,done:true},其中value表示下次应该返回的值,done表示是否还有值可提供。
当没有值可提供时,done为true,如果迭代器在迭代结束时使用了return xxx,则value为xxx,否则为undefined。
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]);
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 }"
// 之后的所有调用
console.log(iterator.next()); // "{ value: undefined, done: true }"
二、迭代器的作用
- 用于依序访问可迭代的对象,可迭代的对象为:Array、Map、Set、String、TypedArray、函数的 arguments对象、NodeList 对象
- 可迭代对象( iterable )内部有一个 Symbol.iterator 属性。这 个 Symbol.iterator 知名符号定义了为指定对象返回迭代器的函数。
- 集合类型有三种迭代器:
entries() :返回一个包含键值对的迭代器,此迭代器是Map类型默认的迭代器;Array把下标做为key;而Set则把值作为key
values() :返回一个包含集合中的值的迭代器,此迭代器是Set与Array的默认迭代器;
keys() :返回一个包含集合中的键的迭代器。
let colors = [ "red", "green", "blue" ];
for (let entry of colors.entries()) {
console.log(entry);
}
- 当使用for of访问对象时,如果没有显示指定使用哪个迭代器,则调用每种类型默认的。
- 在for of中可以使用break语句跳出for of,可使用throw new Error('xxx')终止程序继续运行
三、使用生成器创造迭代器
在function后面加一个*号,然后在函数内部使用yield标识符指定调用next时应该返回的值
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 }"
// 之后的所有调用
console.log(iterator.next()); // "{ value: undefined, done: true }"
生成器一是特殊的函数,因此可以作为对象的方法
var o = {
*createIterator(items) {
for (let i = 0; i < items.length; i ) {
yield items[i];
}
}
};
let iterator = o.createIterator([1, 2, 3]);
yield关键字不能放在生成器内部的函数中,下面代码是错误的
function *createIterator(items) {
items.forEach(function(item) {
// 语法错误
yield item 1;
});
}
四、把不可迭代的对象变成可迭代的
默认情况下,我们自己创建的对象是不可迭代的,可以自己定义一个生成器让对象变成可迭代对象
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);
}
五、迭代器的next和生成器的yield进行通讯
1. 在生成器中,当下一个yield的值依赖上一个yield的结果进行计算时,可以使用迭代器的next(xxx)方法传值来覆盖上一个yield的结果,例子
function *createIterator() {
let first = yield 1;
let second = yield first 2; // 4 2
yield 3;
}
let iterator = createIterator();
console.log(iterator.next()); // 第1个next传值不会生效,因为没有上一个yield
console.log(iterator.next(4)); // first变成了4,结果为"{ value: 6, done: false }"
console.log(iterator.next(5)); // 第3条yield不依赖上一条yield的结果进行计算,传值无效
console.log(iterator.next()); // "{ value: undefined, done: true }"
2.通过iterator.throw()向生成器传递错误来阻止后续的迭代
function *createIterator() {
let first = yield 1;
let second = yield first 2; // 4 2
yield 3;
}
let iterator = createIterator();
console.log(iterator.next()); // 第1个next传值不会生效,因为没有上一个yield
console.log(iterator.throw('后面的别运行了'));
console.log(iterator.next(5)); // 不会执行
console.log(iterator.next()); // 不会执行
3.在生成器中通过try catch屏蔽错误
下面的代码在第二个next前抛出了错误,也就是生成器在执行第一条yield时会出现错误,采用try catch处理后,生成器可以继续向下运行
function *createIterator() {
let first;
try{
first = yield 1;
}
catch(ex){
first=100
}
let second = yield first 2; // 4 2
yield 3;
}
let iterator = createIterator();
console.log(iterator.next());
console.log(iterator.throw('后面的别运行了')); //{value: 102, done: false}
console.log(iterator.next(5)); //没有依赖上一个yield的结果进行计算,返回 {value: 3, done: false}
console.log(iterator.next()); // {value: undefined, done: true}
六、生成器合并
1. 可以将多个生成器合并成一个,方法是新写一个生成器函数,然后把生成器内部的各个yield分别指向不同的生成器。
合并后程序是按顺序执行的,即先把第一个生成器内的所有yield执行完再执行下一个。
function *createOne() {
for(let i=4;i<7;i ){
yield i
}
}
function *createTwo() {
for(let i=7;i<10;i ){
yield i
}
}
function *createAll(){
yield *createOne();
yield *createTwo();
}
var iterator=createAll();
for(let i of iterator){
console.log(i)
}
2.后一个生成器依赖前一个生成器的结果
可以在前一个迭代器中使用return返回一个迭代结束后的值,然后把该值传给第二个迭代器的yield使用
function *createOne() {
for(let i=4;i<7;i ){
yield i
}
return 101;
}
function *createTwo(oneResult) {
for(let i=7;i<10;i ){
yield i oneResult
}
}
function *createAll(){
let result=yield *createOne();
yield *createTwo(result);
}
var iterator=createAll();
for(let i of iterator){
console.log(i)
}
七、任务执行器
每次调用next()时都会执行yield上面的语句,还有一条yield后面的语句。
function* gen(){
console.log('one')
yield 1;
console.log('b')
yield 2;
yield 3;
}
var t=gen();
console.log(t.next());
console.log(t.next());
那么我们能不能做一个任务运行器自动执行next呢?答案是使用递归
let run=function*(generator){
let task=generator();
let result=task.next();启动任务运行器
function autoNext(){
if(!task.done){
result=task.next(result.value)
step();//递归执行下一个next()
}
}
autoNext();
}
run(function*() {
let value = yield 1;
console.log(value); // 1
value = yield value 3;
console.log(value); // 4
});
更多专业前端知识,请上【猿2048】www.mk2048.com
来源:https://blog.csdn.net/weixin_39037804/article/details/102728149