ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

你一定要知道的iterator和generator

2021-12-25 22:03:38  阅读:190  来源: 互联网

标签:console generator iterator next 一定 const 迭代 log


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高级,还有很多需要开发者掌握的地方,可以看看我写的其他博文,持续更新中~

标签:console,generator,iterator,next,一定,const,迭代,log
来源: https://www.cnblogs.com/vigourice/p/15731722.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有