尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

30 seconds of code 实战:用生成器与递归实现扁平化迭代嵌套可迭代对象(flatIterator)

30 seconds of code 实战:用生成器与递归实现扁平化迭代嵌套可迭代对象(flatIterator) 教程文档【免费下载链接】30-seconds-of-codeCoding articles to level up your development skills项目地址https://gitcode.com/gh_mirrors/30/30-seconds-of-code点击查看免费下载导读在 JavaScript 中Symbol.iterator决定了对象是否可迭代iterable但面对数组嵌套数组、数组里混入Set、甚至更深层的混合结构时普通的for...of只能遍历最外层。本文基于 30 seconds of code 的flatIterator片段content/snippets/js/s/flat-iterator.md完整讲解如何用生成器函数 递归 yield*委托实现任意深度、跨容器类型的扁平化迭代并剖析其背后的可迭代协议、边界情况与实战价值。读完你将能写出可复用的flatIterator并理解它与Array.prototype.flat()的本质差异。背景Symbol.iterator与可迭代协议要理解flatIterator先要厘清可迭代对象这一概念。JavaScript 的Symbol.iterator允许你定义对象应当如何被迭代本质上决定了它是否是一个可迭代对象iterable。数组、字符串、Set、Map等都是内建可迭代对象它们都通过Symbol.iterator暴露自己的迭代器。仓库中的 iterators 片段 对迭代器做了系统介绍迭代器必须实现一个next()方法返回形如{ value, done }的对象其中value是序列中的下一个值done是布尔值表示序列是否已被消费完。而 make-iterable 片段 则演示了如何为一个普通对象甚至Number的子类自定义Symbol.iterator例如让数字被展开成其每一位数字组成的数组obj[Symbol.iterator] function* () { for (let key of Object.keys(obj)) yield { [key]: obj[key] }; }; [...obj]; // [ { a: 1 }, { b: 2 }, { c: 3 }]这些知识正是flatIterator的理论基石既然万物皆可迭代那么迭代嵌套的可迭代对象就变成一个值得单独解决的问题。核心思路生成器 递归 yield*委托对于简单的可迭代对象for...of直接遍历即可。但当可迭代对象内部嵌套着其他可迭代对象时例如数组的元素是数组、Set或其他可迭代容器我们需要把它们拉平成一层再遍历。实现这一目标可以借助生成器函数generator function配合递归recursion遍历给定可迭代对象的每一个值检查每个值是否本身是可迭代对象若是则递归地把控制权委托给同一个生成器函数逐层深入若不是则直接产出yield该值。这种检查 → 递归委托 → 否则产出的模式与仓库中 深度优先遍历对象的 walk-through-object 片段 异曲同工——后者同样利用yield*递归委托来下沉到嵌套对象的每一层const walkThrough function* (obj) { const walk function* (x, previous []) { for (let key of Object.keys(x)) { if (typeof x[key] object) yield* walk(x[key], [...previous, key]); else yield [[...previous, key], x[key]]; } }; yield* walk(obj); };可见yield*递归委托是 30 seconds of code 中处理树状/嵌套结构的通用利器也大量出现在 二叉搜索树、二叉树、树、链表 等数据结构的遍历实现中。完整实现与逐行剖析将上述理论落到代码上flatIterator的实现非常紧凑出自 flat-iterator.mdconst flatIterator function* (itr) { for (let item of itr) { if (item[Symbol.iterator]) yield* flatIterator(item); else yield item; } }; const arr [1, 2, [3, 4], [5, [6, [7], 8]], 9, new Set([10, 11])]; [...flatIterator(arr)]; // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11逐行解读const flatIterator function* (itr)定义一个生成器函数参数itr是任意可迭代对象。function*语法意味着每次调用不会立即执行而是返回一个生成器对象在.next()或for...of时惰性地产出值。生成器基础可参考仓库中的 range-generator 片段。for (let item of itr)使用for...of循环遍历给定可迭代对象的值。for...of会自动调用itr[Symbol.iterator]()并逐个取出值。if (item[Symbol.iterator])通过访问item[Symbol.iterator]判断当前值是否可迭代。注意这里只做真值判断——只要item身上存在Symbol.iterator方法哪怕是继承来的就认为它是可迭代对象。yield* flatIterator(item)当item可迭代时用yield*表达式将迭代控制权递归委托给flatIterator自身深入下一层扁平化并把产出的值逐级透传出来。else yield item当item不可迭代例如数字、普通对象、null等时直接产出当前值。[...flatIterator(arr)]利用展开运算符一次性消费生成器把所有值收集进一个新数组。结果按深度优先顺序输出1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11。示例输入arr刻意混合了多种情况普通数字、两层嵌套数组、三层嵌套数组、以及一个Set。flatIterator不需要知道每个容器的具体类型只要它们实现了Symbol.iterator就能被统一拉平——这正是该实现的通用性所在。边界情况与注意事项flatIterator简洁优雅但也存在几个值得开发者留意的边界情况1. 字符串会被展开为字符。字符串是可迭代对象因此flatIterator(ab)会依次产出a、b而不是把ab当作一个整体。在多数场景这符合直觉但若想保留字符串整体需要额外过滤const flatIterator function* (itr) { for (let item of itr) { if (typeof item ! string item[Symbol.iterator]) yield* flatIterator(item); else yield item; } };2. 真值判断可能误判。item[Symbol.iterator]检查只验证属性是否存在并不验证它是否真的是函数。极端情况下若某对象上定义了非函数的Symbol.iterator属性yield* flatIterator(item)会在迭代时抛出 TypeError。防御性写法可以补充typeof item[Symbol.iterator] function判断。3.null与undefined安全。null和undefined没有Symbol.iterator属性访问null[Symbol.iterator]会直接抛 TypeError因此在for...of循环体中访问item[Symbol.iterator]之前item本身必然是可迭代的、至少是已通过for...of正常取出的值不会出现对null的属性访问问题而非null的普通对象由于没有Symbol.iterator会被直接yield出去。4. 深层嵌套与循环引用。实现依赖递归极深嵌套可能引发调用栈溢出同时它不做循环引用检测若数据结构中存在环会无限递归。对来自不可信来源的数据使用前应评估这两类风险。与Array.prototype.flat()/flatMap()的对比许多开发者第一反应是用Array.prototype.flat()但它与flatIterator有本质区别适用范围flat()只能处理数组flatIterator能处理任意实现了Symbol.iterator的可迭代对象数组、Set、Map、生成器、自定义可迭代对象等。深度控制flat()需要显式传深度参数flat(Infinity)表示全部展开flatIterator天然递归到最底层无需指定深度。消费方式flat()返回新数组一次性分配内存flatIterator是惰性生成器可配合for...of逐步消费、或在展开为数组前随时提前终止适合处理大体积或无限序列。结果类型flatMap()只能用于每个元素映射为数组的场景无法处理嵌套的任意可迭代对象。flatIterator更接近协议级的通用方案而flat()只是数组内建的便捷糖。实战应用场景flatIterator的通用性让它天然适用于以下场景树/图结构的深度优先遍历把每个节点的子节点集合视作可迭代对象用同一生成器递归拉平可得到整棵树的叶子序列。这与仓库中 树的先序/后序遍历 使用yield*递归委托的思路完全一致。混合容器的聚合处理当数据源是数组里混着Set、Map甚至自定义可迭代集合时flatIterator统一拉平后再做聚合、去重、统计。惰性迭代流水线作为生成器它可以直接嵌入其他for...of或yield*链中与其他生成器片段如 range-generator组合成复杂的惰性数据管道避免一次性物化整个中间结果。自定义数据结构的迭代统一结合 make-iterable 的思想让自定义类实现Symbol.iterator随后即可无缝接入flatIterator。小结flatIterator用不足五行代码把 JavaScript 的三个核心特性——Symbol.iterator可迭代协议、生成器的惰性求值、yield*的递归委托——完美融合解决了一个真实且高频的工程问题。它不仅是数组扁平化的通用替代更是理解协议驱动设计的极佳入门案例。通过对比仓库中 iterators、make-iterable、range-generator 以及多个 数据结构片段你可以把这一模式进一步迁移到对象深度遍历、树形结构遍历等更广阔的领域。赞分享教程文档【免费下载链接】30-seconds-of-codeCoding articles to level up your development skills项目地址https://gitcode.com/gh_mirrors/30/30-seconds-of-code点击查看免费下载相关推荐30 seconds of code使用递归扁平化Flatten与还原UnflattenJavaScript 嵌套对象30 seconds of code使用递归扁平化Flatten与还原UnflattenJavaScript 嵌套对象 对象是 JavaScript教程文档Rofi 缩略图系统rofi-thumbnails完全指南XDG Thumbnailer、thumbnail:// 协议与 -preview-cmd 自定义预览Rofi 缩略图系统rofi thumbnails完全指南XDG Thumbnailer、 thumbnail:// 协议与 preview cmd 自定教程文档TripoSR 从单张图片 0.5 秒重建 3D 模型完整指南TripoSR 从单张图片 0.5 秒重建 3D 模型完整指南 一张椅子的照片不到 0.5 秒变成可旋转的 3D 模型。TripoSR 是一个免费开源的单图教程文档创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表