es-toolkit 兼容层 multiply 函数完全指南:与 lodash 一致的乘法实现及源码剖析
2026/9/15 13:19:01 网站建设 项目流程

es-toolkit 兼容层 multiply 函数完全指南:与 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 兼容模块(es-toolkit/compat)中的multiply函数展开,系统讲解它的调用方式、边界行为、与原生*运算符的性能取舍,并结合仓库源码与测试用例剖析其底层实现原理,同时介绍函数式版本(es-toolkit/fp)的用法,帮助你安全地从 lodash 迁移或在日常项目中正确使用该 API。

快速导读

multiply用于计算两个数字的乘积,是 lodash 兼容 API 之一。在 multiply 兼容层参考文档 中,官方明确指出:该函数因额外的函数调用开销而运行较慢,推荐直接使用*运算符。因此它真正的价值场景是:保持与 lodash 完全一致的语义(包括参数强制转换、缺省参数、NaN 传播等行为),用于 lodash 迁移或需要在统一 API 抽象下工作的代码。阅读完本文,你将掌握multiply的完整参数语义、返回值规则、源码级实现细节,以及函数式版本es-toolkit/fp中柯里化multiply的组合用法。

一、基本用法与签名

multiply接受两个数字参数,返回它们的乘积:

const result = multiply(value, other);

从 兼容层源码 可以看到其类型签名:

export function multiply(value: number, other: number): number;

该函数从es-toolkit/compat入口导入,并在 compat.ts 中通过export { multiply } from './math/multiply.ts'对外暴露:

import { multiply } from 'es-toolkit/compat'; // 基本乘法 multiply(2, 3); // Returns: 6 multiply(4, 5); // Returns: 20 // 负数处理 multiply(2, -3); // Returns: -6 multiply(-4, -5); // Returns: 20 // 小数处理 multiply(2.5, 4); // Returns: 10 // NaN 处理 multiply(NaN, 3); // Returns: NaN multiply(2, NaN); // Returns: NaN multiply(NaN, NaN); // Returns: NaN

参数说明

参数类型说明
valuenumber乘法中的第一个数字
othernumber乘法中的第二个数字

返回值

返回两个数字相乘的结果(number)。如果任一参数为NaN,则返回NaN

二、为什么官方建议改用*运算符

参考文档在开头放置了醒目的::: warning警告块,这在 es-toolkit 文档体系中属于"不推荐在常规代码中使用"的信号。原因非常直接:

Thismultiplyfunction works slowly due to additional function calls. Use the faster and simpler*operator instead.

翻译过来即:multiply因额外的函数调用而运行较慢,请改用更快更简单的*运算符。es-toolkit 的核心卖点之一就是性能与体积(项目描述中强调"2-3 倍更快、体积缩小最高 97%"),因此在可以直接书写a * b的地方,multiply(a, b)没有任何性能优势

那么multiply存在的意义是什么?答案是兼容性与语义一致性:当你的代码需要以(value, other) => number这样的统一函数签名被传递(例如作为回调、被高阶函数调用),或者你正在从 lodash 迁移、希望获得与 lodash 完全一致的行为时,multiply能保证语义不出现偏差。而这一点,正是下文源码剖析所要揭示的。

三、源码级实现剖析

multiply 兼容层实现 的完整代码如下:

import { toNumber } from '../util/toNumber.ts'; import { toString } from '../util/toString.ts'; export function multiply(value: number, other: number): number { if (value === undefined && other === undefined) { return 1; } if (value === undefined || other === undefined) { return value ?? other; } if (typeof value === 'string' || typeof other === 'string') { value = toString(value) as any; other = toString(other) as any; } else { value = toNumber(value); other = toNumber(other); } return value * other; }

虽然公开签名是(value: number, other: number),但实现里做了大量防御性处理,这正是 lodash 兼容语义的核心所在。

1. 两个参数都缺省时返回 1

if (value === undefined && other === undefined) { return 1; }

multiply()以零参数调用时,返回1(乘法单位元)。这一点在 测试用例 中有明确验证:

// @ts-expect-error - invalid arguments expect(multiply()).toBe(1);

2. 只有一个参数时返回该参数本身

if (value === undefined || other === undefined) { return value ?? other; }

当只传入一个参数时,直接返回那个参数(等价于"乘以 1")。对应测试见 multiply.spec.ts:

// @ts-expect-error - invalid arguments expect(multiply(6)).toBe(6); // @ts-expect-error - invalid arguments expect(multiply(6, undefined)).toBe(6); // @ts-expect-error - invalid arguments expect(multiply(undefined, 4)).toBe(4);

3. 参数强制转换(coercion)逻辑

if (typeof value === 'string' || typeof other === 'string') { value = toString(value) as any; other = toString(other) as any; } else { value = toNumber(value); other = toNumber(other); }

这是兼容 lodash 行为的关键分叉:

  • 只要任一参数是字符串,两个参数都先经过toString处理再相乘;
  • 否则,两个参数都通过toNumber转成数字。

由此引出一个重要行为:multiply('6', '4')返回24,而multiply('x', 'y')返回NaN。测试见 multiply.spec.ts。

其中toNumber来自 util/toNumber.ts,它的特别之处在于:

export function toNumber(value: any): number { if (isSymbol(value)) { return NaN; } return Number(value); }

与原生Number()不同,toNumberSymbol 返回NaN。这使得multiply(0, symbol)multiply(symbol, 0)都会得到NaN,对应测试见 multiply.spec.ts。同理,对象也会被转换为NaNmultiply(0, {})multiply({}, 0)均为NaN,见 multiply.spec.ts。

4. 符号零(-0)的符号保留

一个容易被忽视的兼容细节是:multiply会正确保留-0的符号。测试用例如下(multiply.spec.ts):

const values = [0, '0', -0, '-0']; const expected = [ [0, Infinity], ['0', Infinity], [-0, -Infinity], ['-0', -Infinity], ];

1 / -0 === -Infinity,说明-0的符号没有被破坏。由于实现最终走的是原生value * other,JavaScript 的乘法语义天然保留了这一行为。

5. NaN 传播

由于最终执行value * other,只要任一操作数为NaN,结果即为NaN。参考文档与测试(multiply.spec.ts)都覆盖了multiply(NaN, 3)multiply(3, NaN)multiply(NaN, NaN)三种情况。

四、函数式版本:es-toolkit/fp中的 multiply

除兼容层外,es-toolkit 还在es-toolkit/fp中提供了柯里化版本的multiply,其文档见 fp 版 multiply 参考,实现位于 src/fp/math/multiply.ts:

export function multiply(multiplicand: number): (value: number) => number { return function (value: number): number { return value * multiplicand; }; }

它的签名是multiply(multiplicand) => (value) => value * multiplicand,专为组合场景设计,可配合pipe或作为map的回调使用:

import { map, multiply, pipe } from 'es-toolkit/fp'; // 转换管道中的值 pipe(3, multiply(2)); // => 6 // 作为 map 回调使用 pipe([1, 2, 3], map(multiply(3))); // => [3, 6, 9]

对应测试见 fp/math/multiply.spec.ts,覆盖了管道乘法、map 回调、乘以 0、负乘数等场景:

expect(pipe(3, multiply(2))).toBe(6); expect(pipe([1, 2, 3], map(multiply(3)))).toEqual([3, 6, 9]); expect(pipe(42, multiply(0))).toBe(0); expect(pipe(5, multiply(-2))).toBe(-10);

注意:fp 版本是纯柯里化实现,不包含兼容层的缺省参数、字符串强制转换等防御逻辑——它要求multiplicand是真正的数字。如果需要在管道中处理带强转语义的乘法,请使用兼容层版本。

五、兼容层与 fp 版本的选择建议

场景推荐写法
普通代码中的两个数字相乘直接使用a * b运算符(最快、最简单)
从 lodash 迁移,需要完全一致的语义import { multiply } from 'es-toolkit/compat'
pipe/map等组合场景中乘法import { multiply } from 'es-toolkit/fp'

参考文档中的警告同样适用于 fp 版本:凡是能用*直接书写的地方,都应优先使用运算符。multiply的核心价值是语义兼容可组合性,而不是性能。

六、小结

  • multiply(value, other)返回两数乘积,任一参数为NaN时返回NaN
  • 兼容层实现包含完整的 lodash 兼容语义:零参数返回1、单参数返回自身、字符串参数强制转换、Symbol/对象转NaN-0符号保留;
  • 由于存在额外的函数调用与类型转换开销,官方明确建议常规场景使用*运算符;
  • es-toolkit/fp提供柯里化版本,适合与pipemap等组合工具搭配使用。

相关参考:兼容层文档 multiply、fp 版文档 multiply (FP)、兼容层实现 src/compat/math/multiply.ts、fp 实现 src/fp/math/multiply.ts、测试用例 compat/multiply.spec.ts 与 fp/multiply.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),仅供参考

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

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

立即咨询