es-toolkit 兼容层 eachRight 详解:forEachRight 的 Lodash 别名与逆序遍历实现
2026/9/16 12:57:28 网站建设 项目流程

es-toolkit 兼容层 eachRight 详解: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

eachRight是 es-toolkit 兼容层(es-toolkit/compat)中为 Lodash 用户提供的forEachRight别名,用于从右到左遍历数组、字符串与对象并对每个元素执行回调。本文结合 eachRight 兼容文档 与其目标文档 forEachRight 兼容文档,并对照源码与测试,完整讲解其用法、参数语义、提前终止机制与底层实现原理,帮助你理解它和 es-toolkit 原生forEachRight的区别并做出正确的选型。

eachRight 是什么:别名背后的兼容设计

在 Lodash 中,_.each/_.eachRight分别是_.forEach/_.forEachRight的别名。es-toolkit 的兼容层(compat)为了降低 Lodash 迁移成本,完整保留了这套命名。在 eachRight.ts 中,实现只有一行:

export { forEachRight as eachRight } from './forEachRight.ts';

也就是说,eachRightforEachRight在运行时是同一个函数对象,这一事实由测试直接验证。在 eachRight.spec.ts 中:

import { eachRight } from './eachRight'; import { forEachRight } from './forEachRight'; describe('eachRight', () => { it('should be an alias of forEachRight', () => { expect(eachRight).toBe(forEachRight); }); });

该别名从兼容层入口 compat.ts(第 14 行)统一导出,因此可以这样引入:

import { eachRight } from 'es-toolkit/compat';

由于二者完全等价,本文后续所有行为说明均以forEachRight的完整实现为准。

基本用法:数组、字符串、对象三种集合的逆序遍历

forEachRight(即eachRight)接受一个集合和一个回调,从右到左遍历:

import { forEachRight } from 'es-toolkit/compat'; // 数组逆序遍历 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 // 对象逆序遍历(按自身可枚举字符串键) forEachRight({ a: 1, b: 2, c: 3 }, (value, key) => { console.log(value, key); }); // 输出: 3 'c', 2 'b', 1 'a'

回调函数收到三个参数:

  • value:当前遍历到的元素 / 字符 / 属性值;
  • indexkey:对于数组是数字下标,对于对象是属性键;
  • collection:被遍历的原始集合本身。

兼容层为forEachRight定义了多组重载签名(见 forEachRight.ts),分别覆盖数组(ArrayIterator<T, any>)、类数组(ListIterator<T, any>)、字符串(StringIterator<any>)与对象(ObjectIterator<T, any>)四种形态,保证 TypeScript 调用时的类型收窄。

参数与返回值

根据兼容文档与实现签名,参数定义如下:

参数类型说明
collectionArrayLike<T> \| Record<any, any> \| string \| null \| undefined待遍历的集合,可以是数组、类数组、对象、字符串或null/undefined
callback(item: any, index: any, arr: any) => unknown(可选)对每个元素执行的函数;返回false可提前终止遍历;默认为identity函数

返回值:原集合本身。即传入数组返回该数组,传入对象返回该对象,null/undefined原样返回。例如:

const array = [1, 2, 3]; forEachRight(array, Boolean); // 返回 array 本身(=== 引用相等)

这一点同样被 forEachRight.spec.ts 覆盖:expect(forEachRight(array, Boolean)).toBe(array)

null / undefined 与缺省回调的处理

与 Lodash 行为一致,当集合为nullundefined时不会抛错,而是直接原样返回:

import { forEachRight } from 'es-toolkit/compat'; forEachRight(null, value => console.log(value)); // null forEachRight(undefined, value => console.log(value)); // undefined

当省略回调时,默认使用identity函数,函数仅完成遍历并返回原集合:

const array = [1, 2, 3]; const result = forEachRight(array); // 使用 identity 作为回调 console.log(result === array); // true

提前终止:回调返回 false 即中断遍历

这是forEach系函数在 Lodash 兼容层的重要特性:只要回调显式返回false(严格等于false),遍历立即停止:

import { forEachRight } from 'es-toolkit/compat'; forEachRight([1, 2, 3, 4], value => { console.log(value); if (value === 2) { return false; // 中断遍历 } }); // 输出: 4, 3, 2

在 forEachRight.spec.ts 中,数组与对象场景的提前退出均有测试用例验证(例如can exit early when iterating arrayscan exit early when iterating objects)。注意中断判断使用的是result === false的严格比较(见下方实现代码),因此返回其他假值(如0undefined)并不会中断遍历。

源码级实现原理

兼容层forEachRight的核心实现位于 forEachRight.ts 的最后一个重载函数体内,逻辑非常紧凑:

export function forEachRight<T>( collection: ArrayLike<T> | Record<any, any> | string | null | undefined, callback: (item: any, index: any, arr: any) => unknown = identity ): ArrayLike<T> | Record<any, any> | string | null | undefined { 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; }

从中可以提炼出几个关键设计点:

  1. 空值短路if (!collection) return collection;直接处理null/undefined,这也是兼容层相对原生版本多出的开销之一。
  2. 键列表生成:使用 isArrayLike 判断集合形态——类数组用 range 生成0..length-1的数字键,普通对象用Object.keys取其自身可枚举字符串键。因此数组上自定义的属性(如array.a = 1)不会被遍历,原型链上的属性也不会被遍历(测试should not iterate custom properties on arraysiterates over own string keyed properties of objects分别验证了这两点)。
  3. 逆序循环:从keys.length - 1递减到0,实现从右到左的遍历顺序。
  4. 提前终止result === falsebreak
  5. 返回原集合:遍历结束后返回传入的collection

此外,由于键列表在进入循环前一次性生成,遍历过程中对length的修改或新增属性不会影响本次遍历(对应测试should ignore changes to lengthshould ignore added object properties);同时稀疏数组会被当作稠密数组处理,空缺位置以undefined参与回调(对应测试should treat sparse arrays as dense)。

与 es-toolkit 原生 forEachRight 的区别及选型建议

es-toolkit 在src/array/下提供了只针对数组的原生 forEachRight:

export function forEachRight<T>(arr: readonly T[], callback: (value: T, index: number, arr: T[]) => void): void { for (let i = arr.length - 1; i >= 0; i--) { const element = arr[i]; callback(element, i, arr as T[]); } }

两者差异非常明显:

维度原生forEachRightes-toolkit/array兼容forEachRight/eachRightes-toolkit/compat
适用集合仅数组(readonly T[]数组、类数组、字符串、对象、null/undefined
回调缺省必填可选,默认为identity
提前终止不支持支持(返回false中断)
返回值void原集合
类型检查类型严格需要兼容各种集合形态,签名更宽泛

兼容文档 forEachRight.md 顶部也明确给出了警告:由于需要处理null/undefinedArrayLike类型以及多种条件函数形式,兼容版forEachRight运行更慢;在新代码中应优先使用更快的原生 forEachRight。也就是说:

  • 正在从 Lodash 迁移、需要保持行为完全一致的存量代码,使用es-toolkit/compateachRight/forEachRight
  • 新写的代码,如果只需要逆序遍历数组,直接使用es-toolkit/array的原生forEachRight,以获得更小的体积与更快的速度。

小结

eachRight是 es-toolkit 兼容层对 Lodash 命名习惯的忠实保留,其本质是forEachRight的别名导出(eachRight.ts),二者共享同一实现。掌握其数组、字符串、对象的逆序遍历方式、false提前终止语义、null/undefined直通行为以及基于isArrayLike+Object.keys的键列表机制,即可在 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

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询