es-toolkit/compat 的 rearg 函数:按索引重排函数参数的完整指南
2026/9/15 10:43:13 网站建设 项目流程

es-toolkit/compat 的 rearg 函数:按索引重排函数参数的完整指南

【免费下载链接】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

导读

rearg是 es-toolkit 兼容层(es-toolkit/compat)中用于重排函数调用参数顺序的高阶函数:它根据你指定的索引序列生成一个新函数,新函数在调用时会先把实参按索引重排,再传给原函数执行。本文围绕 docs/compat/reference/function/rearg.md 展开,结合 rearg.ts 源码与 rearg.spec.ts 测试用例,讲解参数形式、边界行为、实现原理以及何时应该避免使用它。读完你将能熟练运用rearg适配第三方回调签名,并理解其与 Lodash 行为对齐的细节。

适用前提:本文示例均基于当前仓库(es-toolkit 的compat兼容层),导入路径为es-toolkit/compat。该兼容层的定位是 1:1 镜像 Lodash 的接口与行为,便于存量 Lodash 代码平滑迁移,详见 docs/compat/intro.md。

基本用法:rearg(func, ...indices)

rearg创建一个新函数,该函数会按照indices指定的顺序重排调用时的实参,再调用原函数。官方文档给出的签名如下:

const rearranged = rearg(func, ...indices);

最典型的场景是交换前两个参数的顺序:

import { rearg } from 'es-toolkit/compat'; const greet = (greeting, name) => `${greeting}, ${name}!`; // Swap argument order (1st, 0th) const rearrangedGreet = rearg(greet, 1, 0); rearrangedGreet('World', 'Hello'); // Returns: "Hello, World!" // Original function remains unchanged greet('Hello', 'World'); // Returns: "Hello, World!"

注意:rearg返回的是新函数,原函数greet不会被修改,其调用语义完全不变。

参数与返回值

  • func(...args: any[]) => any):需要重排参数的原函数。
  • ...indicesArray<number | number[]>):要重排的参数索引序列,支持嵌套数组。
  • 返回值((...args: any[]) => any):一个实参已按索引重排的新函数。

支持数组形式传入索引

除了逐个传入索引,你也可以把索引打包成一个数组:

import { rearg } from 'es-toolkit/compat'; const fn = (a, b, c) => [a, b, c]; // Specify indices using an array const rearranged = rearg(fn, [2, 0, 1]); rearranged('a', 'b', 'c'); // Returns: ['c', 'a', 'b']

索引[2, 0, 1]的含义是:新函数接收的实参中,第 2 个参数作为新函数的第 1 个参数传入原函数,第 0 个参数作为新函数的第 2 个参数,第 1 个参数作为第 3 个参数。测试用例 rearg.spec.ts 中的should accept multiple arrays of indexes还验证了rearg(fn, [2], [0, 1])这种多个数组参数的写法同样有效。

部分重排:只调整部分参数

索引序列的长度可以小于实参个数,未涉及的参数保持原有位置:

import { rearg } from 'es-toolkit/compat'; const fn = (a, b, c, d) => [a, b, c, d]; // Rearrange only the first two arguments const rearranged = rearg(fn, 1, 0); rearranged('first', 'second', 'third', 'fourth'); // Returns: ['second', 'first', 'third', 'fourth']

这里的机制是:索引序列只覆盖前两个参数的位置,剩余参数'third''fourth'按原顺序追加到末尾。对应测试用例should work with fewer indexes than argumentsrearg(fn, [1, 0])rearged('b', 'a', 'c')返回['a', 'b', 'c'])验证了这一行为。

边界行为:不存在的索引与非法索引

不存在的索引视为undefined

如果索引超出了实参的个数,该位置将被填充为undefined

import { rearg } from 'es-toolkit/compat'; const fn = (a, b, c) => [a, b, c]; // Include non-existent index 5 const rearranged = rearg(fn, 5, 1, 0); rearranged('a', 'b', 'c'); // Returns: [undefined, 'b', 'a']

非法索引值同样得到undefined

测试用例should use 'undefined' for non-index values[{}, null, undefined, false, NaN, '', -1, 1.1]这一组非法索引逐一验证,结果均为[undefined, 'b', 'c']——即第一个位置为undefined,其余参数保持原样。这说明rearg并不做索引合法性校验,而是依赖 JavaScript 数组下标越界时天然返回undefined的特性。

不传任何索引时参数顺序不变

测试用例should not rearrange arguments when no indexes are given验证:rearg(fn)rearg(fn, [], [])之后,实参原样传递。

嵌套数组索引会被拍平处理

索引参数支持嵌套数组,rearg会将其扁平化后再使用:

import { rearg } from 'es-toolkit/compat'; const fn = (a, b, c, d) => [a, b, c, d]; // Nested array indices const rearranged = rearg(fn, [1, [2, 0]], 3); rearranged('a', 'b', 'c', 'd'); // Returns: ['b', 'c', 'a', 'd']

等价于展开为索引序列[1, 2, 0, 3],即原实参的bcad依次成为新函数的四个参数。

重复索引也是合法的

测试用例should work with repeated indexes显示索引可以重复使用:rearg(fn, [1, 1, 1])作用于rearged('c', 'a', 'b')会返回['a', 'a', 'a'],即同一个实参可以被重排到多个位置。

源码级解析:rearg 的实现原理

rearg在 src/compat/function/rearg.ts 中的实现非常精简,核心逻辑如下(为便于阅读已简化注释):

export function rearg( func: (...args: any[]) => any, ...indices: Array<Many<number>> ): (...args: any[]) => any { const flattenIndices = flatten(indices); return function (this: any, ...args: any[]) { const reorderedArgs: any[] = flattenIndices.map(i => args[i]).slice(0, args.length); for (let i = reorderedArgs.length; i < args.length; i++) { reorderedArgs.push(args[i]); } return func.apply(this, reorderedArgs); }; }

整个执行流程可以拆解为三步:

  1. 索引拍平(创建期)indices的类型是Array<Many<number>>,其中Many<T>定义于 src/compat/_internal/Many.ts,即T | readonly T[]flatten来自 src/compat/array/flatten.ts,它会基于flattenDepth(..., 1)把嵌套数组拍平一层,得到一维索引数组flattenIndices。这是"嵌套数组索引"支持的底层来源。
  2. 按索引重排(调用期):每次调用新函数时,通过flattenIndices.map(i => args[i])按索引取实参。由于flattenIndices预先计算并缓存在闭包中的,每次调用不会重复拍平索引,这是性能上的一个细节。索引越界时args[i]undefined,这正是上述边界行为的实现基础;同时.slice(0, args.length)截断到实参个数,避免产生多余的undefined
  3. 补全剩余实参reorderedArgs长度不足实参个数时,循环把剩余实参原样追加,实现"部分重排"。最后通过func.apply(this, reorderedArgs)调用原函数,并保留调用方this上下文,因此rearg包装后的函数仍然可以作为对象方法使用。

一个值得注意的组合特性

测试用例should work on functions that have been rearged验证了rearg可以叠加使用rearg(rearg(fn, 2, 1, 0), 1, 0, 2)仍然得到正确结果。由于每次rearg返回的都是普通函数,多层包装在行为上等价于多次参数映射的组合。

实际应用场景与迁移定位

rearg的典型用途包括:

  • 适配第三方回调签名:当库的回调参数顺序与你的函数不一致时,无需改写业务函数,用rearg做一层薄包装即可。
  • 复用 Lodash 存量代码es-toolkit/compat的存在意义就是让旧代码无痛迁移。rearg在 src/compat/compat.ts 中被导出,可以从es-toolkit/compat统一导入,也可以按需单独导入对应入口,详见 docs/compat/intro.md 关于单函数导入的说明。

需要说明的是,根据 docs/compat/intro.md 的设计原则,es-toolkit/compat只保证与 Lodash 的行为与测试用例对齐,并不包含方法链式调用等超出其范围的能力;rearg同样遵循这一边界。

重要建议:优先使用箭头函数

官方文档在开篇就以醒目的警告框提示:rearg创建的是一个重排参数的复杂包装器,可能存在性能开销。对于多数场景,直接用箭头函数手动重排参数会更清晰、更快速:

// 不推荐(为了理解代码还需额外心智负担) const rearrangedGreet = rearg(greet, 1, 0); // 推荐:箭头函数直接重排 const rearrangedGreet = (name, greeting) => greet(greeting, name);

因此,rearg更适合作为迁移 Lodash 存量代码时的过渡方案,或在参数位置确实需要动态配置(索引序列来自运行时数据)的场景下使用;新代码优先考虑箭头函数。这也是 es-toolkit 兼容层的一贯哲学:compat保留与 Lodash 完全一致的行为以方便迁移,而严格 API(es-toolkit主入口)只暴露类型安全、更现代的写法。

小结

rearg(func, ...indices)的核心要点可以归纳如下:

行为说明
基本用法按索引序列重排实参后调用原函数,返回新函数,原函数不变
索引形式支持rearg(fn, 2, 0, 1)rearg(fn, [2, 0, 1]),也支持多个数组参数
嵌套数组索引数组会被拍平(对应flatten实现)
部分重排索引个数少于实参个数时,剩余实参按原顺序追加
越界 / 非法索引结果为undefined,不会抛错
重复索引允许,同一实参可被映射到多个位置
this传递通过func.apply(this, ...)保留调用方上下文
性能建议包装器有额外开销,新代码优先使用箭头函数手动重排

如果你想深入验证上述行为,可以直接运行 rearg.spec.ts 中的全部测试用例,它们覆盖了本文讨论的每一种边界情况。

【免费下载链接】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),仅供参考

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

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

立即咨询