javascript原理--> 迭代器与生成器

103 阅读1分钟

什么是迭代?

  forlet i = 1; i <= 10; ++i) {
     console.log(i);
 }

看上面的一个数组循环,这是一个最基本的迭代。这种迭代需要事先知道数组的数据结构和使用,并且没有办法标识迭代何时终止。

es6为了解决这个问题,支持了迭代器模式

迭代器模式

迭代器模式 描述了一个方案,即可以把有些结构称为“可迭代对象”, 它们实现了 Iterable 接口。

在es6中,暴露了一个属性作为“默认迭代器”, 这个属性必须使用 Symbol.iterator作为健。

以下内置类型都实现了迭代器接口

  • 字符串
  • 数组
  • 映射
  • 集合
  • arguments对象
  • NodeList等DOM集合类型

不需要显示的调用这个工厂函数来生成迭代器,下列语法结构支持迭代。

  • for-of 循环
  • 数组解构
  • 扩展操作符
  • Array.from()
  • 创建集合
  • 创建映射
  • Promise.all()
  • Promise.race()
  • yield* 操作符

let arr = ['foo','bar','baz'];

// for-of 循环
for (let el of arr) {
    console.log(el);
}

// foo
// bar

// 数组解构
let [a,b,c] = arr;
console.log(a,b,c); //foo,bar,baz

// 扩展操作符
let arr2 = [...arr];
console.log(arr2); // ['foo','bar','baz']

// Array.from();
let arr3 = Array.from(arr);
console.log(arr3); // ['foo','bar','baz']

// set构造函数
let set = new Set(arr);
console.log(set); // Set(3){'foo','bar','baz'}

// Map构造函数
let pairs = arr.map((x,i) => [x,i]);
console.log(pairs); // [['foo',0],['bar',1],['baz',2]]
let map = new Map(pairs);
console.log(map); // Map(3) {'foo'=> 0, 'bar'=>1, 'baz'=>2}

迭代器协议

迭代器是一种一次性使用的对象,使用 next() 方法在可迭代对象中遍历数据。

next()方法返回的迭代器对象IteratorResult包含两个属性: done 和 value。

done是一个布尔值,表示是否还可以迭代。

value是可迭代对象的下一个值。

let arr = ['foo'];
let iter = arr[Symbol.iterator]();
console.log(iter.next()); // { done: false, value: 'foo' }
console.log(iter.next()); // { done: true, value: undefined }
console.log(iter.next()); // { done: true, value: undefined }
console.log(iter.next()); // { done: true, value: undefined }

自定义迭代器

class Counter{
    constructor() {}
    [Symbol.iterator]() {
        return {
            next() {
                return {done: false, value: xxx}
            }
        }
    }
}

生成器

生成器是新增的结构,拥有在一个函数块内暂停和恢复代码执行的能力。

function * generratorFn() {}

class Foo {
    *generatorFn() {}
    *  generatorFn2() {}
}

生成器对象一开始处于暂停执行的状态 suspended 与迭代器相似,生成器也实现了Iterator接口,因此具有next()方法。

生成器对象可以通过 yield 中断执行。

可以通过yield 实现输入和输出

function* generatorFn() {
    return yield 'foo';
}
let generatorObject = generatorFn();
console.log(generatorObject.next());  // { done: false, value: 'foo' } 
console.log(generatorObject.next('bar')); // { done: true, value: 'bar' }

可以用 * 号增强 yield的行为,产生一个可迭代对象。

function* generatorFn() {
    yield* [1, 2, 3];
}
let generatorObject = generatorFn();
for (const x of generatorFn()) {
  console.log(x);
}

生成器作为默认迭代器

因为生成器实现了Iterable接口,所以生成器可以作为对象的默认迭代器

class Foo{
    constructor() {
        this.values = [1,2,3]
    }
    
    * [Symbol.iterator]() {
        yield *this.values;
    }
}

const f = new Foo();
for (let v of f) {
    console.log(v);
}

提前终止生成器

  • return
  • throw

function* generatorFn() {
  for (const x of [1, 2, 3]) {
      yield x; 
  }
}
const g = generatorFn();
console.log(g);            // generatorFn {<suspended>}
console.log(g.return(4));  // { done: true, value: 4 }
console.log(g.throw('xxx)) // 
console.log(g);