rest参数与数组操作:从零实现示例

用 rest 参数和数组方法写出更聪明的 JavaScript

你有没有写过这样的函数:明明只想加几个数字,却得先处理arguments?或者想过滤一堆输入,结果被类数组对象折腾得够呛?

function sum() { // 啊!又来了…… var args = Array.prototype.slice.call(arguments); return args.reduce(function(a, b) { => a + b; }, 0); }

这段代码不陌生吧?在 ES6 出现之前,这是标准操作。但今天,我们有更优雅的方式——rest 参数...args)搭配现代数组方法,彻底告别繁琐的参数处理。


从 arguments 到 rest:一次参数处理的进化

JavaScript 的函数一直支持“传多不管”,可问题是,传统方式拿到的是一个叫arguments的“伪数组”。它长得像数组,能用下标访问,但不能直接调用.map().filter()这些好用的方法。

function badExample() { console.log(Array.isArray(arguments)); // false arguments.map(x => x * 2); // TypeError: arguments.map is not a function }

ES6 引入的rest 参数,就是来解决这个问题的。三个点...看似简单,实则改变了我们写函数的方式:

const sum = (...numbers) => numbers.reduce((a, b) => a + b, 0); sum(1, 2, 3, 4); // 10

就这么一行,干净利落。numbers是真正的数组,你想怎么链式调用都行。

rest 参数的三条铁律

  1. 必须在最后
    js function good(a, b, ...rest) { } // ✅ OK function bad(...rest, a, b) { } // ❌ SyntaxError

  2. 只能有一个
    同一个函数里别想着搞两个 rest,语法不允许。

  3. 名字自己定
    ...args...nums...inputs都可以,选个语义清晰的名字比什么都重要。


数组方法加持:让数据自己“流动”起来

有了真正的数组,接下来就该轮到.map.filter.reduce登场了。它们不是新东西,但在 rest 的配合下,威力翻倍。

示例一:邮箱提取器

假设你要从一堆输入中找出合法邮箱:

function extractEmails(...inputs) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return inputs .filter(input => typeof input === 'string') // 只留字符串 .filter(str => emailRegex.test(str)); // 再筛出合规格式 } extractEmails('hello', 'user@example.com', null, 'invalid@.com'); // ['user@example.com']

注意这里的链式调用。每一步都在描述“我们要什么”,而不是“怎么一步步找”。这就是声明式编程的魅力。

示例二:构建高阶校验函数

再来看个更有意思的场景:动态字段验证。

function requiredFields(...fields) { return function(obj) { const missing = fields.filter(field => !(field in obj)); if (missing.length) { throw new Error(`缺少必要字段:${missing.join(', ')}`); } return true; }; } const checkUser = requiredFields('id', 'name', 'email'); checkUser({ id: 1, name: 'Alice' }); // 报错:缺少 email

这个模式在表单验证、API 接口检查中非常实用。你把“规则”提前定义好,运行时只需传入数据即可。


实战技巧:那些文档不会告诉你的事

技巧 1:类型校验也能很灵活

有时候你不只想收参数,还想确保它们类型正确。结合 rest 和类型检查,可以写出健壮的工具函数:

function processNumbers(...nums) { if (!nums.every(n => typeof n === 'number')) { throw new TypeError('所有参数必须是数字'); } return nums.map(n => n ** 2); } processNumbers(1, 2, 3); // [1, 4, 9]

如果你经常做这类判断,甚至可以抽象成通用辅助函数:

const isTypeOf = (type, ...values) => values.every(v => typeof v === type); isTypeOf('string', 'a', 'b', 'c'); // true

技巧 2:日志函数的现代化写法

看看这个带时间戳的日志函数:

function log(level, ...messages) { const time = new Date().toISOString(); const output = messages.map(msg => typeof msg === 'object' ? JSON.stringify(msg) : String(msg) ).join(' '); console[level]?.(`${time} [${level.toUpperCase()}] ${output}`); } log('info', '用户登录', { userId: 123 }); // 输出类似:2025-04-05T10:00:00.000Z [INFO] 用户登录 {"userId":123}

这里的关键在于:
- 第一个参数控制日志级别;
- 剩下的全交给 rest 收集;
- 自动识别对象并序列化,避免[object Object]的尴尬。


常见坑点与避坑指南

❌ 不要滥用 rest

不是每个函数都需要...args。如果参数数量固定,比如(a, b, c),硬加上 rest 反而会让调用者困惑。

✅ 正确使用场景:
- 工具函数(如Math.max那种)
- 事件监听器接收多个回调
- 配置合并函数
- 日志、调试等辅助功能

⚠️ 性能要考虑

虽然 rest 很方便,但如果函数被高频调用且传入大量参数,会生成大数组,增加内存压力。

比如这种写法就要小心:

elements.forEach(el => render(el, ...hugePropsArray));

频繁展开可能影响性能,必要时考虑传引用。

📝 文档别偷懒

用 JSDoc 注明 rest 参数的含义,对团队协作至关重要:

/** * 计算数值平均值 * @param {...number} numbers - 任意数量的数字 * @returns {number} 平均值,无参数时返回 0 */ function average(...numbers) { return numbers.length ? numbers.reduce((a, b) => a + b) / numbers.length : 0; }

IDE 能自动识别这些注释,提升开发体验。


rest vs spread:别再傻傻分不清

很多人混淆这两个操作符,其实记住一句话就行:

rest 是“收进来”,spread 是“打出去”

// 收:把参数收集为数组 function gather(...args) { console.log(args); // [1, 2, 3] } // 打:把数组展开为参数 const arr = [1, 2, 3]; Math.max(...arr); // 相当于 Math.max(1, 2, 3)

它们是一对镜像操作,在函数定义和调用之间架起桥梁。


写在最后

rest 参数看似只是一个语法糖,但它背后代表的是 JavaScript 编程范式的转变:从“手动管理参数”到“让数据自然流动”。

当你开始习惯用(...items) => items.filter(...).map(...)这样的链条去思考问题时,你就已经迈入了现代 JS 开发的大门。

尤其是在 React、Node.js 工具链、配置系统中,这种组合技随处可见。掌握它,不只是为了少写几行代码,更是为了写出更容易理解、测试和维护的逻辑。

如果你现在还在用Array.prototype.slice.call(arguments),不妨停下来,试试这行:

js const fn = (...args) => { /* your logic */ };

你会发现,代码一下子清爽了。

你平时在哪种场景下最喜欢用 rest 参数?欢迎在评论区分享你的实战经验。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1146541.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ResNet18部署案例:智能门禁人脸识别

ResNet18部署案例:智能门禁人脸识别 1. 引言:从通用物体识别到人脸识别的演进 随着深度学习在计算机视觉领域的广泛应用,图像分类技术已从实验室走向实际工程落地。ResNet18作为ResNet系列中最轻量且高效的模型之一,因其结构简洁…

基于 YOLOv8 的二维码智能检测系统 [目标检测完整源码]

基于 YOLOv8 的二维码智能检测系统 [目标检测完整源码] —— 面向复杂场景的 QR Code 视觉识别解决方案一、引言:二维码识别,真的只是“扫一扫”这么简单吗? 在大多数人的认知中,二维码识别等同于手机扫码——对准、识别、跳转。但…

ResNet18实战:智能相册人脸+场景双识别

ResNet18实战:智能相册人脸场景双识别 1. 引言:通用物体识别的现实挑战与ResNet-18的价值 在智能相册、内容管理、图像检索等应用场景中,自动化的图像理解能力是提升用户体验的核心。传统方案依赖人工标注或调用第三方API进行图像分类&…

ResNet18优化技巧:模型微调与迁移学习

ResNet18优化技巧:模型微调与迁移学习 1. 引言:通用物体识别中的ResNet-18价值 在计算机视觉领域,通用物体识别是深度学习最成熟且应用最广泛的任务之一。ImageNet大规模视觉识别挑战赛(ILSVRC)推动了多种经典卷积神…

入门级ALU项目:基于组合逻辑的设计

从零开始造“大脑”:手把手实现一个基于组合逻辑的入门级 ALU你有没有想过,CPU 是怎么把5 3算出来的?它不是靠心算,而是依赖一个叫做ALU的硬件模块——全称是算术逻辑单元(Arithmetic Logic Unit)&#xf…

ResNet18应用案例:电商商品自动分类系统实战指南

ResNet18应用案例:电商商品自动分类系统实战指南 1. 引言:通用物体识别与ResNet-18的工程价值 在电商平台中,每天都有海量的商品图片需要归类。传统的人工标注方式效率低、成本高,且难以应对快速增长的数据量。随着深度学习技术…

ResNet18实战指南:多任务图像分类系统

ResNet18实战指南:多任务图像分类系统 1. 引言:通用物体识别的工程价值与ResNet-18优势 在当前AI应用快速落地的背景下,通用图像分类已成为智能监控、内容审核、辅助搜索等场景的核心能力。尽管大模型如CLIP展现出强大的零样本识别能力&…

ResNet18应用指南:自动驾驶的环境感知

ResNet18应用指南:自动驾驶的环境感知 1. 引言:通用物体识别在自动驾驶中的核心价值 随着自动驾驶技术的快速发展,车辆对周围环境的理解能力成为决定系统安全性和智能水平的关键。环境感知不仅需要检测障碍物、车道线和交通信号&#xff0c…

三极管驱动LED灯电路核心要点:偏置电阻的作用

三极管驱动LED,为什么一定要加基极电阻?你有没有试过直接把MCU的GPIO接到三极管基极,结果烧了芯片或者LED亮度忽明忽暗?这背后很可能就是少了那个看似不起眼的偏置电阻——也就是我们常说的基极电阻Rb。别小看这个几毛钱的电阻&am…

ResNet18性能测试:CPU环境下毫秒级推理实战

ResNet18性能测试:CPU环境下毫秒级推理实战 1. 引言:通用物体识别中的ResNet-18价值定位 在当前AI应用快速落地的背景下,轻量、高效、稳定的图像分类模型成为边缘计算与本地部署场景的核心需求。尽管大模型在精度上不断突破,但其…

多层PCB生产流程图解说明:清晰展示每一关键工序

多层PCB是怎么“炼”成的?一文看懂从铜板到成品的全过程你有没有想过,一块看似普通的电路板,是如何承载起智能手机、5G基站甚至航天器中复杂信号流转的?在那些密密麻麻的走线和微小过孔背后,是一整套精密如钟表、严谨如…

VHDL语言状态机仿真验证方法完整示例

从零构建一个可验证的VHDL状态机:实战全流程详解你有没有遇到过这样的情况?写完一段状态机代码,综合顺利通过,烧进FPGA后却发现行为异常——该跳转的状态没跳,输出信号毛刺频发,甚至直接卡死在某个未知状态…

新手入门首选!HBuilderX安装配置全面讲解

新手也能秒上手!HBuilderX安装与配置全攻略 你是不是也曾在搜索引擎里反复输入“ HBuilderX怎么安装 ”“ 下载后打不开怎么办 ”“ 为什么预览不了网页 ”?别急,这些困扰新手的常见问题,今天一次性给你讲明白。 作为一款…

高速串行通信测试中信号发生器的关键参数设置要点

高速串行通信测试中,如何让信号发生器“说真话”?在5G基站、AI训练集群和超算互联的机柜深处,数据正以每秒上百吉比特的速度奔涌。PCIe 6.0逼近112 Gbps,CPO(共封装光学)链路挑战着传统铜缆的物理极限——在…

ResNet18应用开发:智能零售顾客行为分析

ResNet18应用开发:智能零售顾客行为分析 1. 引言:从通用物体识别到智能零售场景落地 在人工智能驱动的智慧零售时代,理解顾客行为是提升运营效率和用户体验的关键。传统监控系统仅能记录画面,而AI赋能的视觉分析则能“看懂”顾客…

工业环境下的risc-v五级流水线cpu稳定性研究:系统学习

工业级RISC-V五级流水线CPU的稳定性攻坚:从原理到实战你有没有遇到过这样的场景?一台运行在高温车间的PLC控制器,连续工作72小时后突然“死机”,现场排查却发现程序逻辑无误、电源正常、通信链路畅通——问题最终指向了处理器内部…

SPI Flash扇区erase操作的新手教程

SPI Flash扇区擦除操作:从原理到实战的完整指南你有没有遇到过这样的情况?在给设备做固件升级时,新代码写进去却读不出来;或者保存配置参数后重启,发现数据“消失”了”。如果你用的是SPI Flash,那问题很可…

ResNet18实战:电商平台商品自动标注系统

ResNet18实战:电商平台商品自动标注系统 1. 引言:通用物体识别的工程价值 在电商场景中,海量商品图像的自动化处理是提升运营效率的关键环节。传统的人工标注方式成本高、速度慢,难以应对每日新增的数十万级商品图片。而基于深度…

ResNet18实战案例:自动驾驶环境感知系统

ResNet18实战案例:自动驾驶环境感知系统 1. 引言:通用物体识别在自动驾驶中的核心价值 随着自动驾驶技术的快速发展,环境感知作为其“眼睛”的角色愈发关键。车辆必须实时、准确地理解周围世界——从行人、车辆到交通标志、道路类型乃至天气…

ResNet18实战教程:智能零售货架识别系统

ResNet18实战教程:智能零售货架识别系统 1. 引言 1.1 智能零售场景下的图像识别需求 在现代智能零售系统中,自动化的货架监控与商品识别已成为提升运营效率的关键技术。传统人工盘点耗时耗力,而基于计算机视觉的解决方案能够实现实时、精准…