news 2026/9/16 12:15:53

es-toolkit/compat 的 range 函数完全指南:Lodash 互換の数値範囲生成を徹底解説

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
es-toolkit/compat 的 range 函数完全指南:Lodash 互換の数値範囲生成を徹底解説

es-toolkit/compat 的 range 函数完全指南: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 的 Lodash 兼容入口es-toolkit/compat中的range函数(日文文档:docs/ja/compat/reference/math/range.md)。你将掌握range的三种调用签名(单参、双参、三参)、自动步长推断、零步长与小步步长的边界行为、以及作为map等函数 iteratee 的 guard 用法,并深入源码层理解其参数归一化(toFinite)与迭代调用识别(isIterateeCall)的底层实现,最终学会在 Lodash 迁移场景中正确选型。

一、函数定位:Lodash 兼容的range

es-toolkit/compat是 es-toolkit 为平滑迁移 Lodash 而提供的兼容入口。其中的range函数用于创建数值范围数组,行为与_.range保持一致:

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

需要特别注意的是,该 compat 版本在 文档开头即给出警告:由于它需要处理复杂的参数形态与类型转换(如字符串数字、NaNInfinity、假值等),运行速度比 es-toolkit 原生range更慢。文档明确建议:追求性能时应改用更快速、更现代的 es-toolkit 原生 range(从es-toolkit/math导入)。换句话说,compat版本的存在价值是"行为兼容优先",而非"性能优先"。

二、三种调用签名与基本用法

1.range(end):从 0 开始、步长为 1

只传一个参数时,end被当作范围的终点(不含),起点固定为 0,步长自动为 1:

import { range } from 'es-toolkit/compat'; range(4); // Returns: [0, 1, 2, 3] range(0); // Returns: [] range(-4); // Returns: [0, -1, -2, -3]

可以看到,当end为负数时,步长会自动推断为 -1,向负方向生成序列。当end为 0 时返回空数组。

2.range(start, end):从 start 到 end,自动推断步长

传入起止两个参数时,步长根据start < end自动确定为 1 或 -1,无需手动指定方向:

import { range } from 'es-toolkit/compat'; range(1, 5); // Returns: [1, 2, 3, 4] range(5, 1); // Returns: [5, 4, 3, 2] (自動的に-1ずつ減少) range(-2, 3); // Returns: [-2, -1, 0, 1, 2]

起点包含在结果中,终点不包含。

3.range(start, end, step):显式指定步长

三参数形态完全由你控制步长,支持正负方向、零步长以及小数步长:

import { range } from 'es-toolkit/compat'; range(0, 20, 5); // Returns: [0, 5, 10, 15] range(0, -4, -1); // Returns: [0, -1, -2, -3] range(1, 4, 0); // Returns: [1, 1, 1]

其中step = 0是一个值得注意的 Lodash 兼容行为:结果数组中会不断重复start的值(循环次数由(end - start) / 1向上取整决定)。这与原生range(见第四节)会直接抛错的行为截然不同。

小数步长同样被支持:

import { range } from 'es-toolkit/compat'; range(0, 1, 0.2); // Returns: [0, 0.2, 0.4, 0.6, 0.8] range(1, 0, -0.25); // Returns: [1, 0.75, 0.5, 0.25]

4. 参数与返回值一览

参数类型说明
startnumber范围的起始值(包含)。当不传end时,该值被当作end使用
endnumber(可选)范围的结束值(不包含
stepnumber(可选)递增/递减步长,默认根据方向取 1 或 -1

返回值number[],即按指定范围与步长生成的数值数组。

三、作为 iteratee 使用:guard 参数机制

range还可以直接作为map等方法的回调(iteratee)使用,此时会自动命中 guard 分支:

import { range } from 'es-toolkit/compat'; [1, 2, 3].map(range); // Returns: [[0], [0, 1], [0, 1, 2]]

其原理在源码 src/compat/math/range.ts 中清晰可见——实现为函数重载:当传入三个参数(end, index, guard)时,若step存在但不是数字且满足isIterateeCall(start, end, step),则判定为 iteratee 调用,将endstep重置为undefined,从而退回"仅end"的处理路径:

export function range(start: number, end?: PropertyKey, step?: any): number[] { // Enables use as an iteratee for methods like `_.map`. if (step && typeof step !== 'number' && isIterateeCall(start, end, step)) { end = step = undefined; } // ... }

isIterateeCall定义在 src/compat/_internal/isIterateeCall.ts,其判定逻辑是:

  • 第三个参数object必须是一个对象(isObject);
  • 若第二个参数index是数字,则它必须是该对象(类数组)的有效索引(借助 isIndex 校验非负整数且小于长度);
  • index是字符串,则它必须是对象上的属性;
  • 最后校验object[index] === value(通过eq比较)。

只有在以上条件全部满足时才认定是一次 iteratee 调用。对应的测试在 src/compat/math/range.spec.ts 中验证了数组与对象两种集合map场景均能得到[[0], [0, 1], [0, 1, 2]]

四、源码级剖析:compat 版为何"慢"而"兼容"

1. 参数归一化:toFinite

compat 版的第一步是把所有参数强制转为有限数字,调用 src/compat/util/toFinite.ts:

start = toFinite(start); if (end === undefined) { end = start; start = 0; } else { end = toFinite(end); } step = step === undefined ? (start < end ? 1 : -1) : toFinite(step);

toFinite的处理规则包括:

  • 假值(nullundefined''false0)统一归一为 0(-0会被保留);
  • 字符串数字(如'1')经toNumber转为数字;
  • Infinity/-Infinity被钳制为Number.MAX_VALUE的正负值;
  • NaN归一为 0。

这正是测试 src/compat/math/range.spec.ts 中断言的range('1')range('0', 1)range(0, 1, '1')均能给出合理结果的原因——它们分别返回[[0], [0], [0]],而range(NaN)range(NaN, NaN)返回空数组。相比之下,原生range不做这些转换,因此更快。

2. 长度计算与数组填充

步长确定后,结果长度通过如下公式计算:

const length = Math.max(Math.ceil((end - start) / (step || 1)), 0); const result = new Array(length); for (let index = 0; index < length; index++) { result[index] = start; start += step; } return result;

几个关键点:

  • step || 1使得step = 0时按步长 1 计算长度,从而产生重复start的序列(对应range(1, 4, 0)返回[1, 1, 1]);
  • step绝对值大于区间跨度时,Math.ceil后长度可能为 1(测试 range.spec.ts 验证range(1, 5, 20)返回[1]);
  • 负向区间配合负步长同样成立,如range(21, 10, -3)返回[21, 18, 15, 12]
  • 使用new Array(length)预分配容量,避免逐个push带来的扩容开销;
  • -0起始值会被保留(测试 range.spec.ts 验证1 / actual[0]-Infinity)。

五、与原生range的关键差异与选型建议

es-toolkit 原生range(src/math/range.ts)与 compat 版本在 API 相似但语义上有明确区别:

对比项compatrange原生range
导入路径es-toolkit/compates-toolkit/math(见 文档)
参数转换toFinite接受字符串、NaN直接使用数字
step = 0返回重复start的数组抛出错误
小数步长支持要求非零整数,否则抛错
性能因复杂参数处理与类型转换而较慢更快速、更现代

原生实现 src/math/range.ts 中明确通过Number.isInteger(step) || step === 0校验并抛出The step value must be a non-zero integer.,同时默认步长为 1(不根据方向自动推断为 -1),元素生成采用start + i * step的乘法累加方式,不做 iteratee guard 处理。

选型建议

  • 正在从 Lodash 迁移、需要"一行不改"地保持既有_.range行为(含零步长、字符串参数等历史怪癖)时,使用es-toolkit/compat版本;
  • 新写代码、对性能敏感(例如在热循环中生成索引序列)时,优先使用es-toolkit/math的原生range,并遵守其"step 必须为非零整数"的约束;
  • 日常生成连续索引(range(10)得到[0..9])或分页序号(range(1, 11)得到1..10)时,两种版本均可胜任,此时原生版本是更优选择。

六、行为边界速查

结合 源码 与 测试用例,以下是 compat 版range的完整行为边界清单:

  • 单参负数range(-4)[0, -1, -2, -3],步长自动为 -1;
  • 双参降序range(5, 1)[5, 4, 3, 2],无需显式负步长;
  • 零步长range(1, 4, 0)[1, 1, 1]
  • 步长超过区间range(1, 5, 20)[1]
  • 负向大步长range(21, 10, -3)[21, 18, 15, 12]
  • 假值 startrange()range(null)等均按 0 处理(range()返回[]);
  • 类型强制range('1')[0]range('0', 1)[0]range(0, 1, '1')[0]range(NaN)[]
  • iteratee[1, 2, 3].map(range)[[0], [0, 1], [0, 1, 2]],且对数组与普通对象集合均生效。

掌握这些边界行为,你就能在 Lodash 迁移与日常开发中准确预判range的输出,并在需要更高性能时果断切换到原生版本。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 12:15:39

FPGA高速数据通路实战:Spartan-6 DDR3与千兆以太网Verilog设计

简介&#xff1a;一套基于Xilinx Spartan-6 LX16 FPGA开发板的Verilog逻辑例程合集&#xff0c;面向使用ISE 14.7进行FPGA开发的工程师与学习者&#xff0c;重点覆盖DDR3、千兆以太网等高速接口的工程实现。合集收录30个例程&#xff0c;从LED、按键、PLL等入门实验&#xff0c…

作者头像 李华
网站建设 2026/9/16 12:15:34

红外遥控控制电机:从NEC协议解码到PWM调速实战

简介&#xff1a;这套实验例程围绕单片机红外遥控电机远程控制设计&#xff0c;定位于已掌握基础编程、希望深入硬件控制的入门至中级学习者。实验从红外遥控器编码解码入手&#xff0c;演示如何将按键信号解析为控制指令&#xff0c;进而驱动电机启停、调速及换向&#xff1b;…

作者头像 李华
网站建设 2026/9/16 12:14:00

Cadence SIP Layout:系统级封装物理实现核心引擎

1. 项目概述&#xff1a;Cadence SIP Layout不是“画图软件”&#xff0c;而是系统级封装的物理实现中枢Cadence SIP Layout&#xff0c;这个名称里藏着三个关键信号&#xff1a;Cadence——指代Cadence Design Systems公司旗下完整的IC与封装协同设计平台&#xff1b;SIP——S…

作者头像 李华
网站建设 2026/9/16 12:13:25

小米硬件岗笔试题背后的工程师能力图谱

1. 这不是题库搬运&#xff0c;而是硬件工程师能力图谱的现场解剖“小米2026秋招实习-硬件研发工程师-笔试题真题&#xff08;5套&#xff09;”——这个标题在应届生求职季里像一块磁铁&#xff0c;吸住所有盯着小米硬件岗的同学。但我要先泼一盆冷水&#xff1a;把这5套题当“…

作者头像 李华
网站建设 2026/9/16 12:13:05

SRGAN与图像去噪:损失函数、训练调度与工程落地全解析

简介&#xff1a;这份代码基于PyTorch实现了SRGAN图像超分辨率算法&#xff0c;并融合GaN风格的门控去噪思路&#xff0c;适合深度学习初学者、图像处理研究者以及希望复现GAN类生成模型的开发者&#xff0c;无论是课题研究还是工程实践都具备参考价值。资源配套完整的训练、测…

作者头像 李华
网站建设 2026/9/16 12:12:44

STM32驱动NAND Flash的坏块管理实现与Disk接口封装

简介&#xff1a;本资源是一套面向嵌入式开发工程师与STM32进阶学习者的NAND Flash存储管理实战代码包&#xff0c;聚焦NAND Flash在STM32平台上的可靠应用难题&#xff0c;重点解决坏块识别、动态标记、逻辑块重映射及ECC错误校验等核心挑战。资源共151个文件&#xff0c;含51…

作者头像 李华