ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

es-toolkit 的 curryRight 完全指南:从右到左的函数柯里化与 Lodash 兼容实现

es-toolkit 的 curryRight 完全指南:从右到左的函数柯里化与 Lodash 兼容实现 es-toolkit 的 curryRight 完全指南从右到左的函数柯里化与 Lodash 兼容实现【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkitcurryRight是 es-toolkit 中用于从右到左柯里化函数的工具它会创建一个新函数从最后一个参数开始、逐个或成批地接收参数直到收集齐所有参数后调用原函数。本文以 es-toolkit 的 Lodash 兼容版es-toolkit/compat为核心完整讲解其调用方式、与主库版本及curry的差异、占位符placeholder机制、arity参数控制并结合 compat 版源码 与 spec 测试 剖析其底层实现原理与适用场景帮助你写出可复用的柯里化代码。概览两个curryRight两种设计取向es-toolkit 仓库中实际存在两个curryRight它们服务于不同的使用场景版本导入路径特点文档主库版本es-toolkitsrc/function/curryRight.ts只支持一次传一个参数实现极简、速度快docs/reference/function/curryRight.mdcompat 兼容版本es-toolkit/compatsrc/compat/function/curryRight.ts支持占位符、arity校验、任意数量的参数组合行为与 Lodash 对齐但较慢本文docs/compat/reference/function/curryRight.md官方文档在 compat 参考文档 开头明确给出了一条警告compat 版因复杂的占位符处理、参数个数验证与参数合成逻辑而运行较慢如果不需要占位符应优先使用更快的主库curryRight或手写闭包。这一定位决定了本文的写作基调——先讲清 compat 版能做什么再说明何时不必用它。从源码的导出关系看curryRight同时通过 src/compat/compat.ts、src/browser.ts 和 src/function/index.ts 三条路径对外暴露你可以按项目需要选择es-toolkit/compat、浏览器构建或主库入口。基本用法从最后一个参数开始柯里化compat 版curryRight的调用形式为const curriedFunction curryRight(func, arity);其中func是被柯里化的函数arity可选指定函数参数个数省略时使用func.length。import { curryRight } from es-toolkit/compat; // 基本使用 function subtract(a, b, c) { return a - b - c; } const curriedSubtract curryRight(subtract); // 从右最后一个参数开始柯里化 console.log(curriedSubtract(1)(2)(5)); // 5 - 2 - 1 2 console.log(curriedSubtract(1, 2)(5)); // 5 - 2 - 1 2 console.log(curriedSubtract(1)(2, 5)); // 2 - 5 - 1 -4 console.log(curriedSubtract(1, 2, 5)); // 1 - 2 - 5 -6观察上例可发现 compat 版的一个重要特性它允许每次调用传入一个或多个参数并支持多种拆分方式。第一次调用传入的 1 并不立即确定a而是先由右侧的5、2确定c、b最后传入的1才落到a上。这与主库版本一次只能传一个参数的行为形成鲜明对比。与curry从左到右的对比用除法函数最能直观体现两种柯里化方向的区别import { curry, curryRight } from es-toolkit/compat; function divide(a, b, c) { return a / b / c; } // 普通 curry从左 const leftCurried curry(divide); console.log(leftCurried(12)(3)(2)); // ((12 / 3) / 2) 2 // curryRight从右 const rightCurried curryRight(divide); console.log(rightCurried(2)(3)(12)); // ((12 / 3) / 2) 2 // 最后传入的 12 成为第一个参数acurry(divide)中12 → a、3 → b、2 → c而curryRight(divide)中2 → c、3 → b、12 → a。两种方向最终都得到相同的计算结果2但参数的接收顺序完全相反。主库版本在 src/function/curryRight.ts 的实现中每次调用仅接收一个参数并通过[arg, ...args]前插的方式累积参数compat 版则在此基础上支持多参数与占位符代价是额外的composeArgs参数合成开销。与主库版本的对比// compat 版本灵活但较慢 import { curryRight } from es-toolkit/compat; const curriedCompat curryRight(subtract); curriedCompat(1, 2)(3); // 支持 curriedCompat(1)(curryRight.placeholder, 3)(2); // 占位符支持 // 主库版本更快但一次只能一个 import { curryRight } from es-toolkit; const curriedMain curryRight(subtract); curriedMain(1)(2)(3); // 支持 curriedMain(1, 2)(3); // 不支持选择建议追求性能、无需占位符时用主库版需要迁移 Lodash 代码、依赖占位符或灵活传参时用 compat 版。占位符placeholder跳过任意参数位置占位符是 compat 版curryRight的核心差异化能力。通过curryRight.placeholder一个默认值为symbol的特殊值见 src/compat/function/curryRight.ts你可以预先固定任意位置的参数并把空位留给后续调用填充import { curryRight } from es-toolkit/compat; function formatMessage(name, action, time) { return ${name} 在 ${time} 执行了 ${action}; } const curriedFormat curryRight(formatMessage); // 用占位符跳过特定位置 const todayAction curriedFormat(今天); const todayLoginAction todayAction(curryRight.placeholder, 登录); console.log(todayLoginAction(张三)); // 张三 在 今天 执行了 登录 // 先固定时间 const morningFormat curriedFormat(上午9点); console.log(morningFormat(发表评论, 李四)); // 李四 在 上午9点 执行了 发表评论第一个示例中curriedFormat(今天)固定了最右侧的time参数随后用curryRight.placeholder跳过name位置、同时固定action 登录最后一次调用todayLoginAction(张三)时占位符位置被自动填入张三。整个过程不需要按照从左到右的声明顺序传参这正是右柯里化 占位符组合的价值所在。从源码看占位符的解析发生在 composeArgs 函数 中它统计已累积参数中占位符的数量用新传入的参数按序回填占位符空位未用尽的新参数则追加到参数列表末尾。对应的测试覆盖在 curryRight.spec.tsshould support placeholders与should persist placeholders后者验证了占位符在多次调用间持续生效const curried curryRight(fn); const ph curried.placeholder; expect(curried(4)(2, ph)(1, ph)(3)).toEqual([1, 2, 3, 4]); expect(curried(a, ph, ph, ph)(b)(ph)(c)(d)).toEqual([a, b, c, d]);实战场景一数组处理从右柯里化天然适合先固定尾部参数的数据处理模式import { curryRight } from es-toolkit/compat; // 从数组末尾取出指定数量的元素 function takeFromEnd(array, count, separator , ) { return array.slice(-count).join(separator); } const curriedTake curryRight(takeFromEnd); // 先固定分隔符 const takeWithComma curriedTake(, ); // 再固定数量 const takeLast3 takeWithComma(3); const fruits [苹果, 香蕉, 橙子, 葡萄, 猕猴桃]; console.log(takeLast3(fruits)); // 橙子, 葡萄, 猕猴桃 // 使用不同分隔符 const takeWithDash curriedTake( - ); console.log(takeWithDash(2, fruits)); // 葡萄 - 猕猴桃这里takeFromEnd(array, count, separator)的声明顺序是数组在前、配置在后而curryRight让你可以先注入配置分隔符、数量最后才提供数据从而派生出takeLast3这样可复用的专用函数。这正是部分应用partial application的典型收益。实战场景二函数组合与日志系统将变化最小的参数放在函数声明的最右侧就能用curryRight构建稳定的专用函数import { curryRight } from es-toolkit/compat; // 日志输出函数 function logWithPrefix(message, level, timestamp) { return [${timestamp}] ${level}: ${message}; } const curriedLog curryRight(logWithPrefix); // 固定当前时间 const currentTimeLog curriedLog(new Date().toISOString()); // 按级别创建 logger const errorLog currentTimeLog(ERROR); const infoLog currentTimeLog(INFO); const debugLog currentTimeLog(DEBUG); console.log(errorLog(数据库连接失败)); console.log(infoLog(服务器启动)); console.log(debugLog(处理用户请求));timestamp与level属于每个日志实例都相同的配置项只有message变化。通过curryRight一次柯里化即可派生出errorLog、infoLog、debugLog三个专用函数避免在每个调用点重复传入时间与级别。实战场景三函数式编程流水线借助curryRight将map、filter、reduce包装为数据在最后的版本可以让组合代码的阅读顺序与执行顺序一致import { curryRight } from es-toolkit/compat; const mapWith curryRight((array, fn) array.map(fn)); const filterWith curryRight((array, predicate) array.filter(predicate)); const reduceWith curryRight((array, reducer, initial) array.reduce(reducer, initial)); const numbers [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const double x x * 2; const isEven x x % 2 0; const sum (acc, val) acc val; // 组合流水线右侧优先 const processNumbers nums { return reduceWith(filterWith(mapWith(nums, double), isEven), sum, 0); }; console.log(processNumbers(numbers)); // 所有数翻倍 → 过滤偶数 → 求和由于curryRight固定的是最右侧参数mapWith(nums, double)中nums先被确定double随后补入最终返回吃一个数组吐一个变换后数组的函数供filterWith、reduceWith继续嵌套。实战场景四API 请求构建器柯里化常被用于分步配置请求参数import { curryRight } from es-toolkit/compat; function makeRequest(url, method, headers, body) { return fetch(url, { method, headers, body }); } const curriedRequest curryRight(makeRequest); // 先设置 body const withJsonBody curriedRequest(JSON.stringify({ data: test })); // 添加 headers const withHeaders withJsonBody({ Content-Type: application/json, Authorization: Bearer token123, }); // 设置 POST 方法 const postRequest withHeaders(POST); // 最终使用 postRequest(/api/data) .then(response response.json()) .then(data console.log(data));makeRequest(url, method, headers, body)的声明顺序与构建过程恰好相反curryRight允许你从body开始逐步回填headers、method最后只剩url一个可变入口。这比手写一个每次都要传四个参数的函数更符合配置一次、复用多次的实际诉求。指定 arity控制柯里化深度当函数的func.length不能准确反映期望的参数个数例如存在 rest 参数或默认参数时可以通过第二个参数显式指定import { curryRight } from es-toolkit/compat; function variableArgsFunction(a, b, c, ...rest) { return { a, b, c, rest }; } // 将参数个数限制为 3忽略 rest const curriedFixed curryRight(variableArgsFunction, 3); // 从右往左依次接收 c, b, a console.log(curriedFixed(3)(2)(1)); // { a: 1, b: 2, c: 3, rest: [] }compat 版对arity的规范化逻辑位于 curryRight 实现arity默认取func.length随后经过Number.parseInt转为整数若结果为NaN或小于 1 则归零。spec 中should coerce arity to an integer用例验证了0、0.6、xyz等异常值均会被安全归一化curryRight.spec.ts。同时注意源码注释明确说明该方法不会为柯里化函数设置length属性should create a function with a length of 0用例对此有断言curryRight.spec.ts。底层原理compat 版如何工作compat 版curryRight的完整执行流程可概括为三个阶段全部证据来自 src/compat/function/curryRight.ts入口归一化L134-L143arity默认取func.length经Number.parseInt取整非法值归零同时保留一个可作迭代器使用的guard参数供map等集合方法将curryRight直接作为 iteratee 使用spec 中should work as an iteratee用例见 curryRight.spec.ts。递归柯里化L145-L183每次调用先统计真实参数个数总参数数减去占位符数若未达到arity则调用makeCurryRight返回新的包装函数继续等待参数达到目标后若以new调用则通过new func(...args)构造实例否则用func.apply(this, args)保持this绑定。参数合成composeArgsL185-L208将新传入参数按序回填到累积参数中的占位符空位多余的参数追加到末尾从而支持任意顺序与任意拆分方式的传参。与主库版 src/function/curryRight.ts 相比主库版对func.length为 0 或 1 的函数直接返回原函数免去无意义的包装并且每次只接收一个参数、用[arg, ...args]前插累积无占位符、无arity校验因此更快compat 版则为了 Lodash 兼容付出了占位符过滤、整数强制转换与composeArgs合成的额外开销——这正是文档警告慢的原因所在。此外spec 还验证了若干工程细节should ensure new curried is an instance of func保证柯里化函数可作为构造函数使用curryRight.spec.tsshould use this binding of function验证与bind组合时的this传递curryRight.spec.tsshould work with partialed methods验证与partial/partialRight的协作curryRight.spec.ts。手动柯里化更快的替代方案文档特别提示当不需要占位符与灵活传参时手写闭包通常是最快的选择// 使用 curryRight const curriedSubtract curryRight((a, b, c) a - b - c); // 手动闭包更快从右 const manualCurryRight c b a a - b - c; // 两者结果相同 console.log(curriedSubtract(1)(2)(5)); // 2 console.log(manualCurryRight(1)(2)(5)); // 2手动闭包把柯里化完全展开成显式的嵌套箭头函数没有占位符过滤、没有参数合成、没有arity校验运行时开销趋近于零。如果你的函数签名固定、参数顺序明确这通常是更优解只有在需要 Lodash 兼容行为或占位符能力时才应选用 compat 版curryRight。参数与返回值速查参数funcFunction要从右到左柯里化的函数。aritynumber可选函数的参数个数省略时使用func.length。返回值Function { placeholder: symbol }从右到左柯里化的函数可通过其placeholder属性控制参数位置用于占位符。结合本文的全部示例与源码分析你可以根据场景做如下决策追求最大性能且无占位符需求时用主库版curryRight或手动闭包需要兼容 Lodash 行为、占位符、任意拆分传参、arity控制时用es-toolkit/compat的curryRight。【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表