es-toolkit compat 版 forEachRight 完全指南:数组、字符串、对象的逆向遍历与 Lodash 兼容迁移
【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit
本篇指南围绕 es-toolkit 的 Lodash 兼容入口es-toolkit/compat提供的forEachRight(别名eachRight)展开,完整覆盖其在数组、字符串、对象、类数组与null/undefined上的逆向遍历行为、提前中断机制、回调签名、参数与返回值约定,并结合仓库源码与测试用例剖析其底层实现原理,帮助你在从 Lodash 迁移到 es-toolkit 时,安全、正确地使用这一逆向遍历工具。
一、为什么需要 compat 版 forEachRight
es-toolkit 将 API 划分为"原生现代版"与"Lodash 兼容版"(compat)两套入口。forEachRight在两个入口中都有提供,但定位不同:
- 原生版从
es-toolkit/array导入,位于 src/array/forEachRight.ts,只支持数组,实现极其轻量; - 兼容版从
es-toolkit/compat导入,位于 src/compat/array/forEachRight.ts,行为与 Lodash 完全对齐。
需要特别注意的是,官方文档(docs/ja/compat/reference/array/forEachRight.md)开篇给出了明确的性能警告:
兼容版
forEachRight由于要处理null/undefined、ArrayLike类型以及多种条件函数形式,运行速度会更慢。在不需要 Lodash 兼容语义的场景下,应优先使用更快、更现代的 es-toolkit 原生 forEachRight。
也就是说:新项目首选原生版,只有从 Lodash 迁移、或需要保持既有行为一致性时,才使用 compat 版。
二、基本用法:数组、字符串、对象
forEachRight从右向左遍历集合,并对每个元素执行回调函数,适用于"需要从集合尾部开始处理"的场景。
import { forEachRight } from 'es-toolkit/compat'; // 数组:逆序遍历,输出 value 和 index forEachRight([1, 2, 3], (value, index) => { console.log(value, index); }); // 输出: 3 2, 2 1, 1 0 // 字符串:逆序遍历每个字符 forEachRight('abc', (char, index) => { console.log(char, index); }); // 输出: 'c' 2, 'b' 1, 'a' 0 // 对象:按自有可枚举键的逆序遍历,回调收到 value 和 key forEachRight({ a: 1, b: 2, c: 3 }, (value, key) => { console.log(value, key); }); // 输出: 3 'c', 2 'b', 1 'a'回调函数接收三个参数
无论集合类型如何,回调统一接收三个参数(内部类型定义见 src/compat/_internal/):
| 参数 | 含义 |
|---|---|
value | 当前正在处理的元素 / 字符 / 属性值 |
index | 数组索引、字符串字符索引或对象属性键(key) |
collection | 调用forEachRight时的原始集合 |
各集合类型对应的迭代器类型为ArrayIterator<T, R>(数组,(value: T, index: number, collection: T[]) => R)、ListIterator<T, R>(类数组)、StringIterator<R>(字符串)与ObjectIterator<T, R>(对象,(value: T[keyof T], key: string, collection: T) => R)。
三、null 与 undefined 直接原样返回
compat 版对null与undefined采取了宽容策略:不抛错、不执行回调,直接返回原值。这在 Lodash 风格的链式代码中非常实用,可以避免每次调用前的手动判空。
import { forEachRight } from 'es-toolkit/compat'; forEachRight(null, value => console.log(value)); // 返回 null forEachRight(undefined, value => console.log(value)); // 返回 undefined该行为在测试用例中得到了验证(src/compat/array/forEachRight.spec.ts 中should return the input collection if null or undefined is passed)。
四、回调返回 false 可提前中断遍历
与 Lodash 一致,compat 版forEachRight支持"提前退出":当回调返回严格等于false的值时,遍历立即终止。这适合在逆向查找某个满足条件的元素后立即停止的场景。
import { forEachRight } from 'es-toolkit/compat'; forEachRight([1, 2, 3, 4], value => { console.log(value); if (value === 2) { return false; // 中断遍历 } }); // 输出: 4, 3, 2测试用例 src/compat/array/forEachRight.spec.ts 中can exit early when iterating arrays与can exit early when iterating objects分别验证了数组和对象上的提前退出行为。
五、别名 eachRight
eachRight是forEachRight的完整别名,二者指向同一个函数对象。从es-toolkit/compat导入时两种写法等价:
import { eachRight } from 'es-toolkit/compat'; eachRight([1, 2, 3], value => console.log(value)); // 3, 2, 1其实现仅是简单的重导出(src/compat/array/eachRight.ts),测试用例 src/compat/array/eachRight.spec.ts 中should be an alias of forEachRight直接断言eachRight === forEachRight。
六、参数与返回值
| 项目 | 说明 |
|---|---|
collection | ArrayLike<T> \| Record<any, any> \| string \| null \| undefined。要遍历的集合,可以是数组、类数组、对象、字符串或null/undefined |
callback | (item: any, index: any, arr: any) => unknown,可选。对每个元素执行的函数;返回false时中断遍历。默认值为identity函数,即不传回调时仅完成遍历、不产生副作用(测试should use identity function when no callback is provided验证了这一点) |
| 返回值 | 原始集合本身(ArrayLike<T> \| Record<any, any> \| string \| null \| undefined),便于链式调用 |
七、源码实现解析:核心原理
compat 版forEachRight的主实现位于 src/compat/array/forEachRight.ts,逻辑非常紧凑,核心步骤如下:
if (!collection) { return collection; } const keys: PropertyKey[] = isArrayLike(collection) ? range(0, collection.length) : Object.keys(collection); for (let i = keys.length - 1; i >= 0; i--) { const key = keys[i]; const value = (collection as any)[key]; const result = callback(value, key, collection); if (result === false) { break; } } return collection;从实现中可以提炼出几个关键设计:
先构造索引/键快照,再倒序迭代:数组与类数组通过
range(0, length)生成下标序列,对象则通过Object.keys取键列表。keys在进入循环前一次性生成,因此迭代期间对length的修改、或新增的属性都不会影响遍历——测试should ignore changes to length与should ignore added object properties分别验证了这两点。isArrayLike决定迭代策略:符合类数组判定(长度是0到MAX_SAFE_INTEGER之间的整数)的集合按下标遍历,其余值(如-1、1.1、MAX_SAFE_INTEGER + 1作为length的情况)回退到Object.keys按键遍历。测试should use isArrayLike to determine whether a value is array-like覆盖了这一边界。稀疏数组按稠密处理:由于下标序列由
range(0, length)生成,稀疏数组中的空洞位置也会以undefined参与遍历(测试should treat sparse arrays as dense验证了[1, , 3]会被依次访问3、undefined、1)。数组的自定义属性不参与遍历:数组上额外挂载的命名属性(如
arr.a = 1)不会被迭代(测试should not iterate custom properties on arrays),因为数组走的是下标序列而非Object.keys。对象仅遍历自有可枚举字符串键:使用
Object.keys意味着原型链上的属性会被排除。测试iterates over own string keyed properties of objects验证了继承自Foo.prototype的属性不会被访问。提前中断条件为严格相等:
result === false才中断,因此返回0、''、null、undefined等 falsy 值都不会误触中断。
八、compat 版与原生版 forEachRight 的差异对照
原生版 src/array/forEachRight.ts 只面向数组,两者差异如下:
| 维度 | compat 版(es-toolkit/compat) | 原生版(es-toolkit/array) |
|---|---|---|
| 支持集合类型 | 数组、类数组、字符串、对象、null/undefined | 仅数组(含readonly数组) |
null/undefined | 原样返回,不报错 | 不适用(类型上不允许) |
提前中断(返回false) | 支持 | 不支持 |
| 默认回调 | identity,可不传 | 必须传回调 |
| 返回值 | 返回原始集合 | 返回void |
| 实现复杂度 | 较高(多分支 + 类型处理) | 极简(单个倒序for循环) |
因此,纯数组且无需中断语义时,应优先使用原生版以获得最佳性能;需要 Lodash 完全兼容行为(对象、字符串、判空、中断)时,再选择 compat 版。
九、总结
compat 版forEachRight是 es-toolkit 为 Lodash 迁移场景提供的完整逆向遍历实现:它支持数组、字符串、对象与类数组,宽容处理null/undefined,支持回调返回false提前中断,并提供eachRight别名。其实现以"先快照键、再倒序迭代"为核心,附带稀疏数组稠密化、忽略迭代期间结构变更等 Lodash 兼容语义。理解这些行为边界(详见 src/compat/array/forEachRight.spec.ts),可以让你在迁移与日常开发中更准确地预期遍历结果;而在不依赖这些兼容语义的场景,请记得切换回更快的原生 forEachRight。
【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考