Javascript 进阶--迭代器&生成器

193 阅读2分钟

迭代器

在 JavaScript 中,迭代器是一个对象,它定义一个序列,并在终止时可能返回一个返回值。

说明

迭代器对象含有next方法。利用next()方法返回具有valuedone属性的对象。

  • value: 序列中的next值
  • done: 表示序列是否结束,如果是最后一个值为true

特点

  • 如果 value 和 done 一起存在,则它是迭代器的返回值。
  • 一旦迭代器产生终止值之后,再对调用next()应该继续返回{done:true}
  • 一旦创建,迭代器对象就可以通过重复调用 next()显式地迭代

示例: 自定义迭代器

自定义迭代器需满足的条件

  1. 迭代器对象必须有 next方法
  2. next方法中必须返回具有valuedone属性的对象
  3. 当迭代器产生终值的时候,在调用next方法还需返回 含有donetrue的对象
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
    let nextIndex = start;
    let iterationCount = 0;

    const rangeIterator = {
       next: function() { // 定义next方法
           let result;
           if (nextIndex < end) {
               result = { value: nextIndex, done: false }
               nextIndex += step;
               iterationCount++;
               return result; // done为false,可继续迭代
           }
           return { value: iterationCount, done: true } // 返回执行次数和done, 终止迭代
       }
    };
    return rangeIterator; // 返回迭代器对象
}

// 使用
let it = makeRangeIterator(1, 10, 2);

let result = it.next();
while (!result.done) {
 console.log(result.value); // 1 3 5 7 9
 result = it.next();
}

console.log("Iterated over sequence of size: ", result.value); // 5

生成器

可以定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态(比迭代器很简洁好用)

说明

生成器最初调用时,生成器不执行任何代码,而是返回一个Generator的迭代器。 通过调用生成器的next()方法消耗值时,Generator 函数将执行,直到遇到 yield 关键字

特点

  • 使用 function* 语法编写,结合yield关键字
  • 生成器函数在执行时能暂停,后面又能从暂停处继续执行
  • yield后面紧跟迭代器要返回的值yield* 则表示将执行权移交给另外一个生成器对象(yield后面需要紧跟可迭代对象时使用) 示例: 使用function*+yield定义生成器
function* idMaker(){
  var index = 0;
  while(index<3)
    yield index++;
}

var gen = idMaker();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // undefined

function* 定义生成器函数,yield后面紧跟需要迭代器返回的值

属性方法介绍

.next(value)

调用next方法时传值

  • 传递给 next() 的参数值会被 yield 接收。要注意的是,传给第一个 next() 的值会被忽略
  • 调用next()方法时,如果传入了参数,那么这个参数会传给上一条执行的 yield 语句左边的变量(也就是下修改上一次yeild的值)

示例:

function *gen(){
    yield 10;
    x=yield 'foo';
    yield x;
}

var gen_obj=gen();
console.log(gen_obj.next());// 执行 yield 10,返回 10
console.log(gen_obj.next());// 执行 yield 'foo',返回 'foo'
console.log(gen_obj.next(100));// 将 100 赋给上一条 yield 'foo' 的左值,即执行 x=100,返回 100
console.log(gen_obj.next());// 执行完毕,value 为 undefined,done 为 true

.return(value)

(返回具有valuedone属性的对象)

  • 已经处于完成状态的生成器调用return(value),生成器将保持在完成状态
  • 调用return()时提供参数,则value的值就是最后的返回值,否则和调用next()相同效果
const createReturnVal = ()=>{
  function* gen() {
    yield 1;
    yield 2;
    yield 3;
  }
  
  var g = gen();
  console.log(g.next()); // { value: 1, done: false }
  console.log(g.next()); // { value: 2, done: false }
  console.log(g.next()); // { value: 3, done: false }
  console.log(g.next()); // { value: undefined, done: true }
  console.log(g.return()); // { value: undefined, done: true }
  console.log(g.return(1)); // { value: 1, done: true }
  console.log(g.next()); // { value: undefined, done: true }
}
createReturnVal()

throw()

(返回具有valuedone属性的对象) 强制生成器抛出异常,并传递应该抛出的异常值。 这个异常将从当前挂起的生成器的上下文中抛出,就好像当前挂起的 yield 是一个 throw value 语句。

说明

  • 抛出的异常可以被try...catch块捕获 示例
function* gen() {
  while(true) {
    try {
       yield 42;
    } catch(e) {
      console.log("Error caught!");
     // console.log(e); // "error"
    }
  }
}

var g = gen();
console.log(g.next()); // { value: 42, done: false }
console.log(g.throw(new Error("Something went wrong"))); 
// "Error caught!"
// {value: 42, done: false}
console.log(g.throw('error')); // "error"

yiled*

用于委托给另一个generator或可迭代对象 一般用于yeild 需要返回可迭代对象时使用(需要二次迭代时使用,可任意直接迭代类数组的可迭代对象,不含object) 基础示例1

function* g3() {
  yield* [1, 2];
  yield* "34";
  yield* arguments;
  // yield* {a:1,length:1}; // 报错:Uncaught TypeError: object is not iterable 
}

var iterator = g3(5, 6);
console.log(iterator.next()); // { value: 1, done: false } // 迭代[1,2]
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: "3", done: false } // 迭代 "34"
console.log(iterator.next()); // { value: "4", done: false }
console.log(iterator.next()); // { value: 5, done: false } 
console.log(iterator.next()); // { value: 6, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

基础示例2

function* g4() {
  yield* [1, 2];
  return "foo";
}
var result;

function* g5() {
  result = yield* g4();
}
var iterator = g5();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: undefined, done: true },
// 此时 g4() 返回了 { value: "foo", done: true }
console.log(result);          // "foo"

生成器和迭代器的区别

  • 自定义的迭代器需要明确的返回包含value和done的对象
  • 自定义生成器比迭代器简介,可以通过yield返回符合规范的包含value和done的对象

生成器和迭代器的使用场景

  • 当需要对一个对象进行迭代时(比如开始用于一个for..of循环中)
  • 遍历/迭代/扩展运算符时

参考 developer.mozilla.org/zh-CN/docs/… developer.mozilla.org/zh-CN/docs/…