搜索

查看: 3120|回复: 11

[JavaScript] 详解JS前端使用迭代器和生成器原理及示例

[复制链接]
发表于 2023-5-4 11:51:08 | 显示全部楼层 |阅读模式
Editor 2023-5-4 11:51:08 3120 11 看全部
目录
  • 正文
  • for of 是干什么用的
  • 可迭代对象是什么?
  • 生成器和迭代器的关系。
  • 让非迭代对象也可以使用for of 进行遍历
  • for循环和for in的关系
  • 总结
    正文
    生成器和迭代器这两个东西平时作为一个切图仔,一直都没有使用到。感觉是只有在面试之前才会的东西。面试过不了几天,再次看这两个词一阵恍惚。
    记忆力退化成这样了么?最大的原因一定是用得少了。然后呢?就是没有真正的理解它们。我对于它们的认知常常有下面这些:
    1. 我常常把迭代器和生成器理解成完全不同的东西。
    2. 我常常把for、forEach、map、reduce和for of混为一谈
    3. 我常常把数组、类数组认为是可迭代对象
    想来要真正的记住它,增加自己的武器库,必须要弄明白这些东西才行。
    我们首先是要搞明白什么for of是干什么用的。
    业务代码确实使用不上,但是如果不理解的话,等真到了可以使用的场景的时候,又是否真的能够运用起来,甚至记起来呢?

    for of 是干什么用的
    所有人都知道一些概念for、forEach、map、reduce这些是可以遍历数组的,for of是用于遍历迭代对象的。如下:
    const arr = [1, 2, 3]
    arr.forEach((item, index) => {
       console.log(item) // 1, 2, 3
       console.log(index) // 0, 1, 2
    })
    而巧合的是for of也可以遍历数组
    for (let key of arr) {
        console.log(key) // 1 2 3
    }
    将arr改变为const obj = { a: 1, b: 2, c: 3 }的时候,两者都没有遍历出结果。
    前者是没有反应,后者会直接报错:TypeError: obj is not iterable。翻译一下,类型错误:obj 不是一个可迭代对象。
    那么什么是可迭代对象呢?

    可迭代对象是什么?
    我们先来看看下面这个例子:
    const itemLi1 = document.getElementByTagName('li')
    const itemLi2 = document.querySelectorAll('li')
    for(let key of itemLi1) {
        console.log(item)
    }
    for(let key of itemLi2) {
        console.log(item)
    }
    也就是说HTMLCollection和NodeList是可以迭代对象。其他的可迭代对象有Array、map、set、string等等。如果说类数组的话,是不是迭代对象呢?
    const arrLike = {
      0: 1,
      1: 2,
      2: 3,
      lenght: 3
    }
    for (let i = 0; i
    for循环打印出了对应的结果,而for of 报错了。类数组不是可迭代的的对象。这又是为什么呢?我们将类数组和HTMLCollection类型打印出来比较一下。

    2023221083434026.png

    2023221083434026.png


    而类数组如下:

    2023221083434027.png

    2023221083434027.png


    它们有一个明显的不同,可迭代对象的原型链上面是包括Symbol.iterator的。而这个就是让数组变成可迭代的根本原因。
    也就是说,当目的对象的原型链上面包括Symbol.iterator的时候,它才是可迭代对象。
    对象是无序的,无序的东西自然不可以迭代
    这里使用到了Symbol类型,它在MDN上面的解释就是用于生成全局唯一的变量。而可迭代对象就是它的使用场景。受它的启发,我们在业务当中,如果需要前端来生成一个唯一ID的时候,再次之前,通常都是创建一个UUID的函数来生成一个唯一ID。Symbol不用这么麻烦,直接使用就可以了。
    由此可知,Array.prototype[Symbol.iterater]这个函数封装了一些东西,使得for of可以将对象的元素给打印出来。
    换一句话来说,就是Array.prototype[Symbol.iterater] = function() {}的执行生成一个迭代器对象。
    也就是说,当Object.prototype也有[Symbol.iterater]的方法的时候,for of也能够遍历它呢?我们来试试看吧。
    Object.ptotoype[Symbol.iterator] = function value() {}
    这不就是生成器的作用么?

    生成器和迭代器的关系。
    ES6给我提高了一个生成器的函数。既然叫做生成器,它生成的东西就是迭代器。
    表现形式如下:
    function * generation(iterableObject) {
        for(let i = 0; i
    由*符号和yield关键字组成。
    当const iterator = generation([1, 2, 3]), 其执行流程如下:
    iterator.next() ==> { value: 1, done: false }
    iterator.next() ==> { value: 2, done: false }
    iterator.next() ==> { value: 3, done: false }
    iterator.next() ==> { value: undefined, done: true }

    到了第四次,value为undefined的时候,done为true(也就是说,当done为true的时候,value一定为undefined)。所以说,yield的作用有两个:
  • 生成一个值,将该值封装成一个对象,而这个对象是{ value: .., done: flase/true }这样的形式。
  • 停下来
    可以明显的看出来,生成器有一个作用,通过next这个接口,可以看到迭代的过程。
    既然说生成器生成了一个迭代器,那么是不是说生成器执行后的结果就是一个迭代器呢?既然是迭代器,自然就可以被for of给遍历。
    for (const key of generation([1, 2, 3]) {
        console.log(key) // 1, 2, 3
    }
    果然可以。
    经典面试题: 自己实现一个next这样的接口呢?
    上面已经有了实现的思路。通过一个标识符和一个判断就能够使用ES5来使用,如下代码片段。
    function generation(iterableObj) {
        let nextIndex = 0
        function next() {}
        return {
            next: () => {
                return nextIndex
    当nextIndex下于数组长度的时候,没有迭代完毕。
    注意:nextIndex++是先跑nextIndex,再自增。
    何为接口,后台给你一个url地址,这个是网络接口。next是设计师给你封装的一个方法,你通过这个方法来达到上吧yield的两个作用,所以next()也是一个接口,前端接口。简单来说,一个封装好的方法就是一个接口。

    让非迭代对象也可以使用for of 进行遍历
    正如第一节所说,Symbol.iterator的方法是迭代器的关键。那么我们也可以给Object挂载上该方法。既然该方法可以让对象变成迭代器,就可以直接使用上面ES5实现next方法的代码片段。
    const obj = {
      a: 1,
      b: 2,
      c: 3
    }
    Object.prototype[Symbol.iterator] = function value() {
      const keys = Object.keys(Object(this))
      let nextIndex = 0
      function next() {
        return nextIndex
    for循环和for in的关系
    for循环和for in 看着很像,其实只是共用了for这个关键字,它们都是JS引擎底层实现的东西。和forEach、map这些是基于for循环的API不同,它们是在实现在for循环之上的。

    总结
  • 生成器generator执行的结果就是一个迭代器
  • 生成器可以是也是由ES5实现的,不是基于底层API
  • 是否是迭代器的关键是Symbol.iterator方法
    以上就是详解JS前端使用迭代器和生成器原理及示例的详细内容,更多关于JS前端迭代器生成器的资料请关注知鸟论坛其它相关文章!
  • 回复

    使用道具 举报

    发表于 2023-6-29 15:21:52 | 显示全部楼层
    xinting_6ym 2023-6-29 15:21:52 看全部
    这东西我收了!谢谢楼主!知鸟论坛真好!
    回复

    使用道具 举报

    发表于 2023-6-29 19:08:09 | 显示全部楼层
    知足常乐77 2023-6-29 19:08:09 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    回复

    使用道具 举报

    发表于 2023-6-29 19:44:00 | 显示全部楼层
    塞翁364 2023-6-29 19:44:00 看全部
    感谢楼主的无私分享!要想知鸟论坛好 就靠你我他
    回复

    使用道具 举报

    发表于 2023-6-29 22:39:27 | 显示全部楼层
    啤酒瓶空了缓 2023-6-29 22:39:27 看全部
    楼主发贴辛苦了,谢谢楼主分享!我觉得知鸟论坛是注册对了!
    回复

    使用道具 举报

    发表于 2023-6-30 01:47:44 | 显示全部楼层
    麻辣鸡翅 2023-6-30 01:47:44 看全部
    其实我一直觉得楼主的品味不错!呵呵!知鸟论坛太棒了!
    回复

    使用道具 举报

    发表于 2023-6-30 09:20:59 | 显示全部楼层
    掌舵的鱼1987 2023-6-30 09:20:59 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    回复

    使用道具 举报

    发表于 2023-7-3 17:21:45 | 显示全部楼层
    心随674 2023-7-3 17:21:45 看全部
    这东西我收了!谢谢楼主!知鸟论坛真好!
    回复

    使用道具 举报

    发表于 2023-7-4 03:36:48 | 显示全部楼层
    老橡树1 2023-7-4 03:36:48 看全部
    这东西我收了!谢谢楼主!知鸟论坛真好!
    回复

    使用道具 举报

    发表于 2023-7-4 13:22:06 | 显示全部楼层
    贺老师 2023-7-4 13:22:06 看全部
    既然你诚信诚意的推荐了,那我就勉为其难的看看吧!知鸟论坛不走平凡路。
    回复

    使用道具 举报

    • 您可能感兴趣
    点击右侧快捷回复 【请勿灌水】
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则 返回列表

    RSS订阅| SiteMap| 小黑屋| 知鸟论坛
    联系邮箱E-mail:zniao@foxmail.com
    快速回复 返回顶部 返回列表