generator是一种非凡的iterator,generator能够代替iterator实现,使代码更为简洁

什么是iterator

iterator叫做迭代器,是用来帮忙某个数据结构进行遍历的对象,这个对象须要合乎迭代器协定(iterator protocol)。

迭代器协定要求实现next办法,next办法有如下要求

  • 0或者1个函数入参
  • 返回值须要包含两个属性,done 和 value。
    当遍历实现时, done 为 true,value 值为 undefined。

迭代器实现原理

  • 创立一个指针对象,指向以后数据结构的起始地位
  • 第一次调用对象的next办法,指针主动指向数据结构的第一个成员
  • 接下来一直调用next办法,指针始终往后挪动,直到指向最初一个成员
  • 每调用next办法返回一个蕴含value 和 done 属性的对象

以下对象就实现了迭代器

const names = ["kiki", "alice", "macus"];let index = 0;const namesIterator = {  next: () => {    if (index == names.length) {      return { value: undefined, done: true };    } else {      return { value: names[index++], done: false };    }  },};console.log(namesIterator.next())console.log(namesIterator.next())console.log(namesIterator.next())console.log(namesIterator.next())

当第四次调用next办法时,此时数据曾经迭代实现,所以迭代器返回 done 为 true

可迭代对象

可迭代对象与迭代器对象不同。

  • 迭代器对象须要合乎迭代器协定(iterator protocol),并且返回next办法。
  • 可迭代对象须要实现iterable protocol协定,即 @@iterator 办法,在代码中通过 Symbol.iterator 实现,这样当数据进行for...of遍历时,就用调用@@iterator办法。

咱们晓得,对象这种数据类型是不能够通过for...of对其遍历的

但如果咱们对它实现了@@iterator办法之后,它就变成了可迭代对象

const obj = {  name: "alice",  age: 20,  hobby: "singing",  [Symbol.iterator]: function () {    let index = 0;    const keys = Object.keys(this);    return {      next: () => {        if (index == keys.length) {          return { value: undefined, done: true };        } else {          const key = keys[index];          index++;          return { value: this[key], done: false };        }      },    };  },};for (let item of obj) {  console.log(item);}const iterator = obj[Symbol.iterator]()console.log(iterator.next())console.log(iterator.next())console.log(iterator.next())console.log(iterator.next())

可迭代对象的实现中包含了迭代器对象。

原生可迭代对象

简略来说,能够通过for...of遍历的就是可迭代对象,原生可迭代对象包含:数组、字符串、arguments、set、map

const arr = ["kiki", "alice", "macus"];for (let item of arr) {  console.log("数组", item);}const str = "hello";for (let s of str) {  console.log("字符串", s);}function foo() {  for (let arg of arguments) {    console.log("arguments", arg);  }}foo(1, 2, 3, 4);const mapEntries = [  ["name", "alice"],  ["age", "20"],];const map = new Map(mapEntries);for (let m of map) {  console.log("map", m);}const set = new Set(arr);for (let s of set) {  console.log("set", s);}

以上都是原生可迭代对象

可迭代对象的用处

可迭代对象有以下用处

  • for...of遍历
  • 开展语法
  • 解构赋值
  • 创立其余类型的对象,如array和set
  • Promise.all也能够执行可迭代对象

对象能够应用开展语法和解构赋值,但它并不是可迭代对象,而是es9中独自实现的属性

const iteratorObj = {  names: ["kiki", "alice", "macus"],  [Symbol.iterator]: function () {    let index = 0;    return {      next: () => {        if (index == this.names.length) {          return { value: undefined, done: true };        } else {          return { value: this.names[index++], done: false };        }      },    };  },};for (let item of iteratorObj) {  console.log('for..of遍历可迭代对象:',item);}const newArr = [...iteratorObj];console.log('开展语法:',newArr);const [name1, name2, name3] = iteratorObj;console.log('解构赋值:',name1, name2, name3);const set = new Set(iteratorObj);const arr = Array.from(iteratorObj);console.log('set:',set);console.log('array:',arr)Promise.all(iteratorObj).then((value) => {  console.log("promise.all:", value);});

以上办法都是获取next办法中的value值

自定义类的迭代

想要类变成可迭代对象,在类办法中增加 Symbol.iterator 办法并实现就能够了

class Student {  constructor(name, age, hobbies) {    this.name = name;    this.age = age;    this.hobbies = hobbies;  }  push(hobby) {    this.hobbies.push(hobby);  }  [Symbol.iterator]() {    let index = 0;    return {      next: () => {        if (index === this.hobbies.length) {          return { done: true, value: undefined }        } else {          return { value: this.hobbies[index++], done: false };        }      },    };  }}const student = new Student('kiki', '16', ['singing'])student.push('swimming')student.push('tennis')for(let item of student){  console.log(item)}

此时能够通过for..of办法遍历类中的hobbies属性

什么是generator

generator叫做生成器,能够用来管制函数什么时候执行和暂停。

生成器函数也是函数,然而和一般函数之间存在如下区别

  • 生成器函数之间须要增加一个*
  • 执行须要应用一个变量来接管, 每应用一次 next()办法执行一段代码
  • 通过 yield 关键字暂停函数,yield既能够传参, 又有返回值
  • 返回值是一个生成器(generator),生成器是一种非凡的迭代器

以上代码实现了生成器函数

function* foo(){  console.log('开始执行')  yield  console.log('world')  yield  console.log('完结执行')}const generator = foo()console.log(generator.next())console.log(generator.next())console.log(generator.next())

调用next办法时,返回值与迭代器统一,为蕴含 value 和 done 的对象,此时value为undefine,因为yield后没有加上返回值

yield传参和返回值

通过next办法能够将参数传递到生成器函数中,通过yield能够返回数据

function* foo(value1){  console.log('开始执行')  const result1 = yield value1  const result2 = yield result1  const result3 = yield result2  console.log('完结执行')}const generator = foo('hello')console.log(generator.next('世界'))console.log(generator.next('merry'))console.log(generator.next('christmas'))console.log(generator.next('done'))

能够看到第一个next获取的value值是通过生成器函数传递的,而不是第一个next办法执行时的参数,所以value值为"hello"而不是"世界"

generator的其余办法

  • throw办法用于抛出异样(须要在生成器函数中捕捉)
  • return办法用于中断生成器函数的执行
function* createGenerator() {  console.log("开始执行");  try {    yield "hello";    console.log("hello");  } catch (error) {    yield error;  }  yield "world";  console.log("完结执行");}const generator = createGenerator();console.log(generator.next());console.log(generator.throw("throw"));console.log(generator.return("return"));

应用return办法后,done变为true,value就变成了return函数中传递的值

生成器代替迭代器

生成器是一种非凡的迭代器,通过生成器能够在某些场景做一些替换,使代码更为简洁

// 迭代器实现next办法function createArrayIterator(arr) {  let index = 0;  return {    next: function () {      if (index == arr.length) {        return { value: undefined, done: true };      } else {        return { value: arr[index++], done: false };      }    },  };}// generator遍历暂停函数function* createArrayGenerator(arr) {  for(let item of arr){    yield item  }}// yiled 语法糖function* createArraYield(arr) {  yield* arr}const arr = ['alice', 'kiki', 'macus']const iterator = createArrayIterator(arr)console.log(iterator.next())console.log(iterator.next())console.log(iterator.next())console.log(iterator.next())const generator = createArrayGenerator(arr)console.log(generator.next())console.log(generator.next())console.log(generator.next())console.log(generator.next())const yiledGen = createArraYield(arr)console.log(yiledGen.next())console.log(yiledGen.next())console.log(yiledGen.next())console.log(yiledGen.next())

以上三种形式所实现的性能是统一的

类中应用生成器

类中遍历的形式由迭代器改为生成器

class Student {  constructor(name, age, hobbies) {    this.name = name;    this.age = age;    this.hobbies = hobbies;  }  push(hobby) {    this.hobbies.push(hobby);  }  *[Symbol.iterator]() {    yield* this.hobbies  }}const student = new Student('kiki', '16', ['singing'])student.push('swimming')student.push('tennis')for(let item of student){  console.log(item)}

以上就是iterator和generator的用法和分割,对于js高级,还有很多须要开发者把握的中央,能够看看我写的其余博文,继续更新中~