• JS中的迭代器、可迭代对象、生成器


    迭代器

    迭代器(iterator),是使用户在容器对象(container,例如链表或数组)上可以遍历访问的对象,使用该接口无需关心容器对象的内部实现细节。 

    从上面定义上来看,迭代器是帮助我们对某个数据结构进行遍历的对象

    迭代器本身也是一个具体的对象,只不过这个对象需要符合迭代器协议,通俗的讲就是该对象必须含有个next方法,next方法返回一个拥有done(boolean)和value(一个具体的值或undefined)两个属性的对象。迭代结束done为true,否则为false

    创建迭代器 

    下面我们写一个创建数组迭代器的方法 

    1. function createArrayIterator(arr) {
    2. let index = 0
    3. return {
    4. next() {
    5. if(index < arr.length) {
    6. return { done: false, value: arr[index++] }
    7. } else {
    8. return { done: true, value: undefined }
    9. }
    10. }
    11. }
    12. }
    13. let nums = [1,2,3]
    14. let names = ['小明', '小红', '小芳']
    15. let numsIterator = createArrayIterator(nums)
    16. console.log(numsIterator.next()) // { done: false, value: 1 }
    17. console.log(numsIterator.next()) // { done: false, value: 2 }
    18. console.log(numsIterator.next()) // { done: false, value: 3 }
    19. console.log(numsIterator.next()) // { done: true, value: undefined }
    20. let namesIterator = createArrayIterator(names)
    21. console.log(namesIterator.next()) // { done: false, value: '小明' }
    22. console.log(namesIterator.next()) // { done: false, value: '小红' }
    23. console.log(namesIterator.next()) // { done: false, value: '小芳' }
    24. console.log(namesIterator.next()) // { done: true, value: undefined }

    将一个非可迭代对象 转化为 可迭代对象 

    在这之前,我们可以提前考虑一下,为什么for of可以遍历数组而不能遍历对象,就是因为数组本身是有迭代器的,而对象则没有 

     

     

    从上面两个示例应该就能明白了,对象中没有 iterable 迭代器,因为不可for of 遍历 

    如果我们也想将一个非可迭代对象 转化为 可迭代对象 只需要在其中增加 Symbol.iterator 方法即可如下:

    1. let info = {
    2. name: 'wft',
    3. age: 18,
    4. height: 1.88,
    5. // 增加 Symbol.iterator 方法 转为一个可迭代对象
    6. [Symbol.iterator]() {
    7. let values = Object.values(this)
    8. let index = 0
    9. let iterator = {
    10. next() {
    11. if(index < values.length) {
    12. return { done: false, value: values[index++] }
    13. } else {
    14. return { done: true, value: undefined }
    15. }
    16. }
    17. }
    18. return iterator
    19. }
    20. }
    21. let infoIterator = info[Symbol.iterator]()
    22. console.log(infoIterator.next()) // { done: false, value: 'wft' }
    23. console.log(infoIterator.next()) // { done: false, value: 18 }
    24. console.log(infoIterator.next()) // { done: false, value: 1.88 }
    25. console.log(infoIterator.next()) // { done: true, value: undefined }
    26. for(let item of info) {
    27. console.log(item)
    28. }
    29. //wft
    30. //18
    31. //1.88

    注意:一定是 [Symbol.iterator] 规定好的,不可修改

    arguements也是一个可迭代对象 

    是不是迭代对象就看有没有迭代器即可 

    1. function foo() {
    2. console.log(arguments)
    3. for(let item of arguments) {
    4. console.log(item)
    5. }
    6. }
    7. foo(1,2,3)

    生成器 

    生成器函数:

    • function 后面会跟上符号 *
    • 代码的执行可以被yield控制(类似于断点)
    • 生成器函数默认在执行时,返回一个生成器对象(并不会执行函数内部的代码)
    •  要想执行函数内部的代码,需要生成器对象调用它的next方法操作
    • 当调用next,执行内部函数代码时,当遇到yield时,就会中断执行

    调用生成器函数,将返回生成器对象,但是并不会执行函数内部的代码,生成器对象中也有个next方法,调用next方法才会去执行方法内部的代码,遇到yield将终止执行 。示例如下:

    1. // 生成器函数
    2. function * fn() {
    3. console.log(111)
    4. yield
    5. console.log(222)
    6. yield
    7. console.log(333)
    8. }
    9. // 调用生成器函数,返回生成器对象
    10. // 注意:并没有执行fn中的代码
    11. const generator = fn()
    12. // 生成器也有一个 next 方法,调用next方法,将执行 fn 内部的代码,遇到 yield 将终止执行
    13. generator.next() // 111
    14. generator.next() // 222
    15. generator.next() // 333

    yield 叫产出,可以产出数据,也就是在 yield 后面跟上数据,那么它将会作为next方法的返回值,示例如下:

    1. // 生成器函数
    2. function * fn() {
    3. // 第一次调用next方法,会执行这的代码...
    4. yield '哈哈哈'
    5. // 第二次调用next方法,会执行这的代码...
    6. yield {name: 'wft', age: 18}
    7. // 第三次次调用next方法,会执行这的代码...
    8. }
    9. const generator = fn()
    10. console.log(generator.next()) // { value: '哈哈哈', done: false }
    11. console.log(generator.next()) // { value: { name: 'wft', age: 18 }, done: false }

    我们调用next方法的时候,也是可以传递参数的,那么这个参数就作为yield的返回值返回,示例如下: 

    1. // 生成器函数
    2. function * fn() {
    3. let num1 = yield '哈哈哈'
    4. console.log(num1, 'num1') // 注意这里得调用第二次next方法的时候才会执行 --- 结果:123
    5. let num2 = yield {name: 'wft', age: 18}
    6. console.log(num2, 'num2') // 同样这里得调用第三次next方法的时候才会执行 --- 结果:456
    7. }
    8. const generator = fn()
    9. console.log(generator.next()) // { value: '哈哈哈', done: false }
    10. console.log(generator.next(123)) // { value: { name: 'wft', age: 18 }, done: false }
    11. console.log(generator.next(456)) // { value: undefined, done: true }

    使用生成器代替上面的迭代器 

    1. function * createArrayGenerator(arr) {
    2. // 方式一:
    3. for(let i = 0; i < arr.length; i++) {
    4. yield arr[i]
    5. }
    6. // // 方式二:yield语法糖写法 等同于方式一
    7. // yield * arr
    8. }
    9. let nums = [1,2,3]
    10. // nums生成器对象
    11. let numsGenerator = createArrayGenerator(nums)
    12. console.log(numsGenerator.next()) // { value: 1, done: false }
    13. console.log(numsGenerator.next()) // { value: 2, done: false }
    14. console.log(numsGenerator.next()) // { value: 3, done: false }
    15. console.log(numsGenerator.next()) // { value: undefined, done: true }
  • 相关阅读:
    LeetCode 2558. 从数量最多的堆取走礼物
    EasyExcel 三分钟搞定导入导出
    使用springboot框架读取和导入excel表格
    【CV】第 4 章:图像深度学习
    【C++进阶】map和set( 万字详解)—— 上篇
    4. 条件查询
    云原生之nacos架构一览解读
    JimuReport积木报表 v1.7.2 版本发布,低代码报表工具
    六、Spring Boot 整合 NoSQL(4)
    【Mediator模式】C++设计模式——中介者模式
  • 原文地址:https://blog.csdn.net/m0_51431448/article/details/127880800