es-toolkit BigInt 降序生成:rangeRight 用法、源码原理与边界行为全解析
2026/9/17 12:11:00 网站建设 项目流程

es-toolkit BigInt 降序生成:rangeRight 用法、源码原理与边界行为全解析

【免费下载链接】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/bigint子路径下提供了专门面向BigInt的数值工具集,rangeRight便是其中用于按降序生成BigInt序列的函数。本文以官方文档 docs/ja/reference/bigint/rangeRight.md 为骨架,结合 src/bigint/rangeRight.ts 源码与 src/bigint/rangeRight.spec.ts 测试,完整讲解它的三种调用形式、与range的镜像关系、step边界规则以及底层实现原理,帮助你安全处理超出Number.MAX_SAFE_INTEGER的降序区间枚举。

函数签名与三种调用形式

rangeRight与普通数值类型的rangeRight语义一致,但输入输出全部使用BigInt,因此可以精确表示任意大的整数区间。官方文档给出的类型签名如下:

const numbers = rangeRight(end); const numbers = rangeRight(start, end); const numbers = rangeRight(start, end, step);

从源码 src/bigint/rangeRight.ts 可以看出,函数通过 TypeScript 重载声明了三种形态,最终合并为一个实现:

export function rangeRight(start: bigint, end?: bigint, step = 1n): bigint[] { if (end == null) { end = start; start = 0n; } if (step === 0n) { throw new Error('The step value must be a non-zero bigint.'); } const length = bigIntRangeLength(start, end, step); const result = new Array<bigint>(length); for (let i = 0; i < length; i++) { result[i] = start + BigInt(length - i - 1) * step; } return result; }

注意一个易被忽略的设计:当只传入一个参数时,该参数被当作end,而start自动置为0n。这一点与range完全一致,也解释了为什么rangeRight(4n)得到的是[3n, 2n, 1n, 0n]而非包含4n的序列。

专属入口:为什么只能从 es-toolkit/bigint 导入

文档中特别注明:该函数仅能从es-toolkit/bigint独家导入,以避免与其他数值类型的同名函数产生潜在冲突。确实,es-toolkit 在es-toolkit/compat中还提供了面向NumberrangeRight(见 benchmarks/performance/rangeRight.bench.ts),两者重名但类型不同。因此使用时请务必显式从子路径导入:

import { rangeRight } from 'es-toolkit/bigint';

它由 src/bigint/index.ts 统一导出,与rangeclampinRangesum等 BigInt 工具并列。

使用一:rangeRight(end) —— 从 end 前一位倒数到 0n

当你希望从end的紧邻前一个值开始,一路递减到0n(含)时,使用单参数形式。文档示例:

import { rangeRight } from 'es-toolkit/bigint'; console.log(rangeRight(4n)); // [3n, 2n, 1n, 0n] console.log(rangeRight(0n)); // []
  • 参数endbigint)—— 范围的结束值,不包含在结果中。
  • 返回值bigint[]—— 从end紧邻前一位递减到0nBigInt数组。

end0n时,end紧邻前一位是-1n,低于起点0n,没有任何可生成的值,因此返回空数组。测试 src/bigint/rangeRight.spec.ts 对rangeRight(4n) === [3n, 2n, 1n, 0n]做了断言。

使用二:rangeRight(start, end) —— 倒数到任意起点

当你的降序区间终点不是0n时,传入两个参数指定区间。文档示例:

import { rangeRight } from 'es-toolkit/bigint'; console.log(rangeRight(2n, 5n)); // [4n, 3n, 2n] console.log(rangeRight(-3n, 0n)); // [-1n, -2n, -3n]
  • 参数
    • startbigint)—— 范围的起始值,包含在结果中(作为序列的最后一个元素);
    • endbigint)—— 范围的结束值,不包含在结果中(作为序列的第一个元素的前驱)。
  • 返回值bigint[]—— 从end紧邻前一位递减到start(含)的数组。

第二个例子直观体现了负数区间:rangeRight(-3n, 0n)0n之前(即-1n)开始递减到-3n,结果为[-1n, -2n, -3n]。注意此时的start-3n)小于end0n),序列是递减方向,这与数学直觉一致。

使用三:rangeRight(start, end, step) —— 自定义步长与镜像关系

当你需要以1n以外的间隔枚举时,使用三参数形式。官方文档特别强调了一个重要性质:返回结果与相同参数调用range的结果完全相反

import { range, rangeRight } from 'es-toolkit/bigint'; console.log(rangeRight(0n, 10n, 2n)); // [8n, 6n, 4n, 2n, 0n] console.log(rangeRight(5n, 0n, -1n)); // [1n, 2n, 3n, 4n, 5n] // 始终是相同参数 range 的逆序 console.log(rangeRight(0n, 10n, 3n)); // [9n, 6n, 3n, 0n] console.log(range(0n, 10n, 3n)); // [0n, 3n, 6n, 9n]

其中第二个例子展示了负步长的语义:rangeRight(5n, 0n, -1n)等价于先求出升序(此处实为降序)的range(5n, 0n, -1n) === [5n, 4n, 3n, 2n, 1n],再反转得到[1n, 2n, 3n, 4n, 5n]。也就是说,rangeRight并不改变步长的“方向语义”,它只是把range的产出整体反转——正步长对应降序输出,负步长反而得到升序输出。

  • 参数
    • startbigint)—— 范围的起始值,包含;
    • endbigint)—— 范围的结束值,不包含;
    • stepbigint,可选)—— 计数间隔,默认1n
  • 返回值bigint[]——range(start, end, step)的降序版本。

步长背离 end 时返回空数组

文档明确指出:当步长指向远离结束值的方向时,没有任何可生成的值,返回空数组

import { rangeRight } from 'es-toolkit/bigint'; console.log(rangeRight(0n, 5n, -1n)); // []

这里start = 0nend = 5n,步长-1n指向负方向,而end在正方向,步长背离结束值,故结果为空。对应测试见 src/bigint/rangeRight.spec.ts。同理可推,rangeRight(5n, 0n, 1n)也会得到[]

错误处理:step 为 0n 时抛出异常

由于步长为0n会导致死循环或歧义,rangeRightrange一样会直接抛出错误:

import { rangeRight } from 'es-toolkit/bigint'; rangeRight(0n, 5n, 0n); // Error: The step value must be a non-zero bigint.

从 src/bigint/rangeRight.ts 可以看到,该错误在参数归一化之后、长度计算之前被抛出,错误信息为The step value must be a non-zero bigint.。测试 src/bigint/rangeRight.spec.ts 用toThrowError对该信息做了精确断言。

源码级原理:一次分配 + 等差数列填充

相比逐项push的朴素实现,rangeRight采用了“先算长度、预分配数组、再按等差公式填充”的策略,这正是 es-toolkit 保持高性能的原因之一。关键步骤:

  1. 长度计算:调用内部工具 src/_internal/bigIntRangeLength.ts,它用纯BigInt算术实现Math.ceil((end - start) / step)的天花板除法:
export function bigIntRangeLength(start: bigint, end: bigint, step: bigint): number { const difference = end - start; if (step > 0n) { return difference <= 0n ? 0 : Number((difference + step - 1n) / step); } return difference >= 0n ? 0 : Number((difference + step + 1n) / step); }

正步长时,若difference <= 0n直接返回 0(即“步长背离 end”的情形);负步长时,若difference >= 0n返回 0。注释中也说明这与 Number 版实现Math.ceil((end - start) / step)语义完全对齐,只是换成了无精度损失的 BigInt 运算。

  1. 预分配new Array<bigint>(length)一次性确定数组容量,避免动态扩容。

  2. 反向填充:核心公式result[i] = start + BigInt(length - i - 1) * step直接按索引倒推位置,无需先构造升序数组再reverse(),既省一次遍历也省一份临时内存。对比 src/bigint/range.ts 中result[i] = start + BigInt(i) * step的正向填充,两者共用bigIntRangeLength与同样的参数归一化逻辑,结构高度对称。

与 range 的关系及超越安全整数的价值

rangeRight与 range 是一对“镜像”函数:rangeRight(start, end, step)始终等于range(start, end, step).reverse()。测试 src/bigint/rangeRight.spec.ts 用toEqual(range(0n, 10n, 3n).reverse())固化了这个不变量。

两者的共同价值在于BigInt 在任何大小下都保持精确,因此可以安全构建超出Number.MAX_SAFE_INTEGER(即9007199254740991)的区间而不会出现数值悄悄碰撞。例如与 range 配合:

import { range } from 'es-toolkit/bigint'; // Number 无法精确表示这些值,BigInt 可以 console.log(range(9007199254740993n, 9007199254740996n)); // [9007199254740993n, 9007199254740994n, 9007199254740995n]

同理,rangeRight也能对这类超大区间做精确的降序枚举,这在处理时间戳(纳秒级)、哈希值或任意精度业务 ID 的区间倒序场景中非常实用。

性能基准

仓库在 benchmarks/performance/rangeRight.bench.ts 中提供了rangeRight的基准测试,使用 Vitest 的bench分别测量es-toolkit/rangeRightes-toolkit/compat/rangeRightlodash/rangeRightrangeRight(0, 100, 1)场景下的耗时。需要说明的是,该基准针对的是 Number 版rangeRight,但它验证了 es-toolkit 在同类函数上的实现竞争力;BigInt 版虽未被单独纳入该基准,却共享了“预分配 + 等差数列填充”的同一套优化思路。

总结

调用形式示例结果
rangeRight(end)rangeRight(4n)[3n, 2n, 1n, 0n]
rangeRight(start, end)rangeRight(2n, 5n)[4n, 3n, 2n]
rangeRight(start, end, step)rangeRight(0n, 10n, 2n)[8n, 6n, 4n, 2n, 0n]
负步长rangeRight(5n, 0n, -1n)[1n, 2n, 3n, 4n, 5n]
步长背离 endrangeRight(0n, 5n, -1n)[]
step === 0nrangeRight(0n, 5n, 0n)抛出Error

使用要点回顾:

  • es-toolkit/bigint导入,避免与 Number 版同名函数混淆;
  • end永远不包含在结果中,start始终包含;
  • 结果恒等于相同参数range的逆序;
  • step默认1n,不可为0n,背离end时返回空数组;
  • 底层由 bigIntRangeLength 计算长度后预分配数组,通过等差公式一次填满,兼顾正确性与性能。

需要进一步了解正向枚举、区间判断或求和等 BigInt 工具,可继续阅读 docs/ja/reference/bigint/range.md 及 src/bigint/index.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),仅供参考

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

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

立即咨询