你一定要知道的iterator和generator

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和generator
文章图片

可迭代对象
可迭代对象与迭代器对象不同。
  • 迭代器对象需要符合迭代器协议(iterator protocol),并且返回next方法。
  • 可迭代对象需要实现iterable protocol协议,即 @@iterator 方法,在代码中通过 Symbol.iterator 实现,这样当数据进行for...of遍历时,就用调用@@iterator方法。
我们知道,对象这种数据类型是不可以通过for...of对其遍历的
你一定要知道的iterator和generator
文章图片

但如果我们对它实现了@@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())

可迭代对象的实现中包括了迭代器对象。
你一定要知道的iterator和generator
文章图片

原生可迭代对象
简单来说,可以通过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); }

以上都是原生可迭代对象
你一定要知道的iterator和generator
文章图片

可迭代对象的用途
可迭代对象有以下用途
  • 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值
你一定要知道的iterator和generator
文章图片

自定义类的迭代
想要类变成可迭代对象,在类方法中添加 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属性
你一定要知道的iterator和generator
文章图片

什么是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后没有加上返回值
你一定要知道的iterator和generator
文章图片

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"而不是"世界"
你一定要知道的iterator和generator
文章图片

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函数中传递的值
你一定要知道的iterator和generator
文章图片

生成器替代迭代器
生成器是一种特殊的迭代器,通过生成器可以在某些场景做一些替换,使代码更为简洁
// 迭代器实现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())

以上三种方式所实现的功能是一致的
你一定要知道的iterator和generator
文章图片

类中使用生成器
类中遍历的方式由迭代器改为生成器
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
文章图片

【你一定要知道的iterator和generator】以上就是iterator和generator的用法和联系,关于js高级,还有很多需要开发者掌握的地方,可以看看我写的其他博文,持续更新中~

    推荐阅读