百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

JavaScript展开运算符与剩余参数,灵活操作数组与对象的终极利器

myzbx 2025-07-03 18:16 27 浏览

各位IT界的探索者们,你是否在日常编码中遇到过这样的场景:想将两个数组合并,或者给一个对象添加几个新属性,却不得不写一堆重复冗余的代码?又或者,你写了一个函数,希望它能接收任意数量的参数,却发现传统的arguments对象用起来那么不优雅、甚至有点“过时”?在JavaScript的现代世界里,这些曾经的“小烦恼”,如今都有了优雅且强大的解决方案。

今天,我们就来揭秘JavaScript中一对形影不离、却又各司其职的“魔法双子星”——展开运算符(Spread Operator)剩余参数(Rest Parameters)。它们乍一看长得一模一样,都是那三个点...,但在不同的语境下,它们却能分别扮演着“打开包装”和“打包零散”的截然不同角色。它们不仅能让你的代码变得更简洁、更富有表现力,还能让你在处理数组和对象时拥有前所未有的灵活性。准备好了吗?让我们一起探索这对“天生一对”的利器,看看它们是如何彻底改变你写代码的方式的!



一、它们是什么?——长得像,但功能“南辕北辙”!

在深入学习它们各自的“魔法”之前,我们得先搞清楚这对“双胞胎”的本质区别。虽然都长着...这副面孔,但它们的职责却截然不同:

  • 展开运算符(Spread Operator): 顾名思义,它的作用是**“展开”**。它将一个可迭代对象(如数组、字符串)的元素在原地“展开”成一个个独立的个体,或者将一个对象的属性“展开”成一个个独立的键值对。你可以想象成把一个“包装盒”拆开,里面的内容全部散落出来。
  • 剩余参数(Rest Parameters): 与“展开”相反,它的作用是**“收集”**。它将函数调用时传入的、数量不定的多个参数,或者数组/对象解构时“剩余”的部分,收集到一个数组中。这就像是把零散的东西“打包”成一个整体。

划重点: 展开是“分”,剩余是“合”。理解了这一点,你就掌握了这对“双子星”的精髓!


二、展开运算符(Spread Operator):“打开包装”,释放强大能量!

展开运算符让数据操作变得异常灵活和简洁,它主要应用在以下几个场景:

1. 数组的“拷贝”与“合并”

在过去,拷贝数组通常需要slice()方法,合并数组需要concat()方法。有了展开运算符,一切变得如此优雅:

      const originalArray = [1, 2, 3];

// 拷贝数组:创建一个新数组,包含原数组的所有元素(浅拷贝)
const copiedArray = [...originalArray];
console.log(copiedArray); // [1, 2, 3]
console.log(copiedArray === originalArray); // false,是新数组!

// 合并数组:将多个数组的元素展开,组合成一个新数组
const arrayA = [1, 2];
const arrayB = [3, 4];
const combinedArray = [...arrayA, ...arrayB, 5, 6];
console.log(combinedArray); // [1, 2, 3, 4, 5, 6]

// 插入元素:在数组中间插入新元素
const parts = [1, 5];
const newArr = [2, ...parts, 3, 4];
console.log(newArr); // [2, 1, 5, 3, 4]
    

对比传统方法,是不是简洁了不止一星半点?这种非破坏性操作(不改变原数组)在函数式编程和响应式框架中尤其重要。

2. 对象的“拷贝”与“合并”

展开运算符也能完美地应用于对象,实现对象的浅拷贝和属性合并:

      const userProfile = {
  name: '张三',
  age: 30,
  city: '北京'
};

// 拷贝对象:创建一个新对象,包含原对象的所有属性
const copiedProfile = { ...userProfile };
console.log(copiedProfile); // { name: '张三', age: 30, city: '北京' }
console.log(copiedProfile === userProfile); // false

// 合并对象:将多个对象的属性展开,组合成一个新对象
const defaultSettings = { theme: 'dark', notifications: true };
const userSettings = { notifications: false, language: 'zh-CN' };

// 后面的属性会覆盖前面的同名属性
const finalSettings = { ...defaultSettings, ...userSettings };
console.log(finalSettings);
// { theme: 'dark', notifications: false, language: 'zh-CN' }
    

IGNORE_WHEN_COPYING_START

content_copy download

Use code with caution. JavaScript

IGNORE_WHEN_COPYING_END

这对于更新状态(如React/Vue组件中的state),或者合并配置对象非常实用。它让你的更新操作变得异常清晰,并且保持了数据的不可变性。

3. 将可迭代对象转换为参数列表

展开运算符的另一个强大之处在于,它能将一个可迭代对象(如数组)的元素“展开”为函数调用的独立参数:

      const numbers = [10, 20, 5, 30, 15];

// 找出数组中的最大值,无需使用循环
const maxNumber = Math.max(...numbers);
console.log(maxNumber); // 30

// 将字符串展开为数组的元素
const greeting = 'Hello';
const chars = [...greeting];
console.log(chars); // ['H', 'e', 'l', 'l', 'o']
    

IGNORE_WHEN_COPYING_START

content_copy download

Use code with caution. JavaScript

IGNORE_WHEN_COPYING_END

是不是感觉像“魔法”一样?原本需要循环遍历或特定方法才能完成的操作,现在一行代码就能搞定!


三、剩余参数(Rest Parameters):“打包零散”,聚合为一!

剩余参数如同一个“收集器”,它将函数调用时多余的参数,或者解构时未匹配的元素/属性,优雅地收集起来。

1. 函数参数的“收集器”

这是剩余参数最常见的应用场景。它能让你在函数中接收任意数量的参数,并以数组的形式进行处理,彻底告别了“古老”且有些别扭的arguments对象。

      // 接收任意数量的数字并求和
function sum(...nums) { // nums是一个数组
  let total = 0;
  for (const num of nums) {
    total += num;
  }
  return total;
}

console.log(sum(1, 2));         // 3
console.log(sum(1, 2, 3, 4, 5)); // 15

// 结合普通参数
function greet(firstName, lastName, ...hobbies) {
  console.log(`你好,${firstName} ${lastName}!`);
  if (hobbies.length > 0) {
    console.log(`你的爱好有:${hobbies.join('、')}`);
  }
}

greet('李', '明', '阅读', '跑步', '编程');
// 输出:
// 你好,李 明!
// 你的爱好有:阅读、跑步、编程

greet('王', '芳');
// 输出:你好,王 芳!
    

IGNORE_WHEN_COPYING_START

content_copy download

Use code with caution. JavaScript

IGNORE_WHEN_COPYING_END

看到没?...nums就负责把所有传入的参数“打包”成一个名为nums的数组,让函数处理不定数量参数变得如此简洁和直观。

2. 解构赋值中的“收集器”

剩余参数也可以用在数组或对象的解构赋值中,用来收集“剩下”的元素或属性。

      // 数组解构中的剩余参数
const [first, second, ...remainingNumbers] = [10, 20, 30, 40, 50];
console.log(first);          // 10
console.log(second);         // 20
console.log(remainingNumbers); // [30, 40, 50]

// 对象解构中的剩余参数(注意:剩余参数必须是解构的最后一个属性)
const { name, age, ...otherDetails } = {
  name: '小A',
  age: 28,
  occupation: '前端工程师',
  hometown: '广州'
};
console.log(name);         // '小A'
console.log(age);          // 28
console.log(otherDetails); // { occupation: '前端工程师', hometown: '广州' }
    

IGNORE_WHEN_COPYING_START

content_copy download

Use code with caution. JavaScript

IGNORE_WHEN_COPYING_END

这对于从一个复杂的数据结构中取出部分关键信息,同时又想保留其余部分以便后续处理的场景,简直是神器!


四、为什么它们是“天生一对”?——“分”与“合”的艺术!

展开运算符和剩余参数虽然功能相反,但它们常常配合使用,形成一种强大的“分”与“合”的编程模式,尤其在处理不可变数据时大放异彩。

场景一:函数传递参数的完美配合

一个函数用剩余参数接收不定数量的参数,然后将这些参数再用展开运算符传递给另一个函数。

      function logArgs(...args) { // 使用剩余参数收集所有传入的参数
  console.log('所有参数:', args);
  anotherFunction(...args); // 使用展开运算符将收集到的参数再次展开传递
}

function anotherFunction(p1, p2, p3, ...rest) {
  console.log('另一个函数接收到:', p1, p2, p3, rest);
}

logArgs(1, 'hello', true, { id: 1 }, [2, 3]);
// 输出:
// 所有参数: [1, 'hello', true, { id: 1 }, [2, 3]]
// 另一个函数接收到: 1 hello true [{ id: 1 }, [2, 3]]
    

IGNORE_WHEN_COPYING_START

content_copy download

Use code with caution. JavaScript

IGNORE_WHEN_COPYING_END

这种模式让函数的参数传递变得极其灵活,不再受固定参数数量的限制。

场景二:非破坏性地更新数组或对象

这是它们在React/Vue等现代前端框架中被大量使用的原因。通过展开运算符创建一个新数组/对象,同时添加、修改或删除元素/属性,而不会直接修改原始数据。

      const todos = [
  { id: 1, text: '学习JS', completed: false },
  { id: 2, text: '编写博客', completed: true }
];

// 添加一个新任务
const newTodo = { id: 3, text: '发布文章', completed: false };
const updatedTodosAdd = [...todos, newTodo];
console.log('添加后:', updatedTodosAdd);

// 标记任务已完成(通过映射并创建新对象)
const updatedTodosComplete = todos.map(todo =>
  todo.id === 1 ? { ...todo, completed: true } : todo // 使用展开运算符更新属性
);
console.log('更新后:', updatedTodosComplete);

// 删除一个任务
const updatedTodosDelete = todos.filter(todo => todo.id !== 1);
console.log('删除后:', updatedTodosDelete);
    

IGNORE_WHEN_COPYING_START

content_copy download

Use code with caution. JavaScript

IGNORE_WHEN_COPYING_END

这种模式极大地提升了代码的可维护性和可预测性,因为它避免了副作用,让数据流向更加清晰。


五、优势与最佳实践:让代码“会呼吸”

  • 代码简洁性: 显著减少了冗余代码,尤其是数组和对象的合并、拷贝操作。
  • 可读性提升: 语义清晰,一眼就能看出是在“展开”还是在“收集”,意图明确。
  • 促进不可变性: 尤其是在处理数组和对象时,它鼓励创建新的数据副本而不是直接修改原始数据,这对于React、Redux、Vuex等状态管理模式至关重要。
  • 替代老旧方法: 告别arguments对象、Array.prototype.concat、Object.assign()等,用更现代、更直观的方式实现相同功能。

小提醒: 展开运算符进行的数组和对象拷贝都是浅拷贝。这意味着如果你的数组或对象中嵌套了其他对象或数组,那么内层的数据仍然是引用关系。如果需要深拷贝,你可能需要考虑JSON.parse(JSON.stringify(obj))(有局限性)或使用专门的深拷贝库(如Lodash的cloneDeep)。


总结:拥抱“双子星”,驾驭现代JavaScript!

展开运算符和剩余参数,这对看起来相似却功能相反的“魔法双子星”,是现代JavaScript开发中不可或缺的利器。它们极大地提升了我们处理数组和对象的灵活性,简化了函数参数的处理,并促进了更健壮、更可预测的编程范式(尤其是不可变性)。

从今天开始,尝试在你的代码中多思考:这里是需要“展开”数据,还是需要“收集”数据?当你熟练掌握了它们的用法,你会发现自己的JavaScript代码变得更加简洁、高效、富有表现力,仿佛拥有了“点石成金”的能力!

那么,在你的日常开发中,你最常用的是展开运算符还是剩余参数?它们又在哪些场景下给你带来了惊喜呢?欢迎在评论区分享你的经验和看法,我们一起进步,让代码“会呼吸”!

相关推荐

如何设计一个优秀的电子商务产品详情页

加入人人都是产品经理【起点学院】产品经理实战训练营,BAT产品总监手把手带你学产品电子商务网站的产品详情页面无疑是设计师和开发人员关注的最重要的网页之一。产品详情页面是客户作出“加入购物车”决定的页面...

怎么在JS中使用Ajax进行异步请求?

大家好,今天我来分享一项JavaScript的实战技巧,即如何在JS中使用Ajax进行异步请求,让你的网页速度瞬间提升。Ajax是一种在不刷新整个网页的情况下与服务器进行数据交互的技术,可以实现异步加...

中小企业如何组建,管理团队_中小企业应当如何开展组织结构设计变革

前言写了太多关于产品的东西觉得应该换换口味.从码农到架构师,从前端到平面再到UI、UE,最后走向了产品这条不归路,其实以前一直再给你们讲.产品经理跟项目经理区别没有特别大,两个岗位之间有很...

前端监控 SDK 开发分享_前端监控系统 开源

一、前言随着前端的发展和被重视,慢慢的行业内对于前端监控系统的重视程度也在增加。这里不对为什么需要监控再做解释。那我们先直接说说需求。对于中小型公司来说,可以直接使用三方的监控,比如自己搭建一套免费的...

Ajax 会被 fetch 取代吗?Axios 怎么办?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!今天给大家带来的主题是ajax、fetch...

前端面试题《AJAX》_前端面试ajax考点汇总

1.什么是ajax?ajax作用是什么?AJAX=异步JavaScript和XML。AJAX是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX可以使网页实...

Ajax 详细介绍_ajax

1、ajax是什么?asynchronousjavascriptandxml:异步的javascript和xml。ajax是用来改善用户体验的一种技术,其本质是利用浏览器内置的一个特殊的...

6款可替代dreamweaver的工具_替代powerdesigner的工具

dreamweaver对一个web前端工作者来说,再熟悉不过了,像我07年接触web前端开发就是用的dreamweaver,一直用到现在,身边的朋友有跟我推荐过各种更好用的可替代dreamweaver...

我敢保证,全网没有再比这更详细的Java知识点总结了,送你啊

接下来你看到的将是全网最详细的Java知识点总结,全文分为三大部分:Java基础、Java框架、Java+云数据小编将为大家仔细讲解每大部分里面的详细知识点,别眨眼,从小白到大佬、零基础到精通,你绝...

福斯《死侍》发布新剧照 "小贱贱"韦德被改造前造型曝光

时光网讯福斯出品的科幻片《死侍》今天发布新剧照,其中一张是较为罕见的死侍在被改造之前的剧照,其余两张剧照都是死侍在执行任务中的状态。据外媒推测,片方此时发布剧照,预计是为了给不久之后影片发布首款正式预...

2021年超详细的java学习路线总结—纯干货分享

本文整理了java开发的学习路线和相关的学习资源,非常适合零基础入门java的同学,希望大家在学习的时候,能够节省时间。纯干货,良心推荐!第一阶段:Java基础重点知识点:数据类型、核心语法、面向对象...

不用海淘,真黑五来到你身边:亚马逊15件热卖爆款推荐!

Fujifilm富士instaxMini8小黄人拍立得相机(黄色/蓝色)扫二维码进入购物页面黑五是入手一个轻巧可爱的拍立得相机的好时机,此款是mini8的小黄人特别版,除了颜色涂装成小黄人...

2025 年 Python 爬虫四大前沿技术:从异步到 AI

作为互联网大厂的后端Python爬虫开发,你是否也曾遇到过这些痛点:面对海量目标URL,单线程爬虫爬取一周还没完成任务;动态渲染的SPA页面,requests库返回的全是空白代码;好不容易...

最贱超级英雄《死侍》来了!_死侍超燃

死侍Deadpool(2016)导演:蒂姆·米勒编剧:略特·里斯/保罗·沃尼克主演:瑞恩·雷诺兹/莫蕾娜·巴卡林/吉娜·卡拉诺/艾德·斯克林/T·J·米勒类型:动作/...

停止javascript的ajax请求,取消axios请求,取消reactfetch请求

一、Ajax原生里可以通过XMLHttpRequest对象上的abort方法来中断ajax。注意abort方法不能阻止向服务器发送请求,只能停止当前ajax请求。停止javascript的ajax请求...