JS怎么使用展开运算符合并对象
本文详细讲解在 JavaScript 中如何使用 ES6 展开运算符(...)合并对象,包括基础操作步骤、同名属性覆盖规则、浅拷贝机制、空值边界处理及常见注意事项。
...)。只需在新建的对象字面量大括号中,将待合并的源对象依次展开即可:const merged = { ...objA, ...objB };。这种语法是纯函数式的,不仅书写简洁,而且绝不会修改原对象,目前已在前端应用开发中广泛替代了传统的 Object.assign()。
一、展开运算符合并对象的基础语法与操作步骤
合并多个对象的过程非常直观,无需调用复杂的全局函数。其核心操作可以归结为以下三个步骤:
- 准备源对象:明确需要进行数据整合的两个或多个已有对象(例如基础信息与附加属性)。
- 声明新的对象字面量:书写一对花括号
{},这将作为存放合并结果的全新对象容器。 - 使用展开运算符逐一展开:在花括号内,对每一个源对象变量名前加上省略号
...,多个对象之间用逗号隔开。
下面是一个标准的合并示例:
// 1. 准备两个源对象
const userBase = {
id: 1001,
name: "张三"
};
const userContact = {
email: "zhangsan@example.com",
city: "北京"
};
// 2. 在新对象字面量中展开合并
const userProfile = { ...userBase, ...userContact };
// 示例输出结果
console.log(userProfile);
/*
{
id: 1001,
name: "张三",
email: "zhangsan@example.com",
city: "北京"
}
*/
// 验证原对象是否被改变(保持完全不变)
console.log(userBase); // { id: 1001, name: "张三" }
console.log(userContact); // { email: "zhangsan@example.com", city: "北京" }
执行后,JavaScript 运行时会创建一个独立的空对象,并依次提取 userBase 与 userContact 自身的所有可枚举属性,浅拷贝写入到新对象中。源对象 userBase 和 userContact 的内存与内容均保持原样,没有任何副作用。
二、属性重名时的覆盖规则与书写顺序
当合并的多个源对象中包含同名属性键时,展开运算符遵循非常明确的逻辑:后展开的属性值会直接覆盖先展开的属性值。
这一特性非常适用于设置“默认配置与自定义覆盖”的开发场景。我们将系统默认选项写在前面,用户传入的自定义选项写在后面,即可自动完成配置的覆写与合并。
// 默认系统配置
const defaultOptions = {
timeout: 1000,
cache: true,
retries: 3
};
// 用户传入的个性化配置(包含同名键 timeout 和 retries)
const customOptions = {
timeout: 5000,
retries: 5,
debug: true
};
// defaultOptions 在前,customOptions 在后
const requestConfig = {
...defaultOptions,
...customOptions
};
// 示例输出
console.log(requestConfig);
/*
{
timeout: 5000, // 来自 customOptions,成功覆盖默认的 1000
cache: true, // 来自 defaultOptions,未被覆盖
retries: 5, // 来自 customOptions,成功覆盖默认的 3
debug: true // 来自 customOptions 的独有新属性
}
*/
除了展开已有对象,我们还可以在大括号中直接书写单个属性。只要写在展开表达式之后,该属性同样会覆盖之前展开的同名值:
// 在展开后直接追加或重写特定属性
const updatedUser = {
...userProfile,
city: "上海", // 覆盖 userProfile 中的 city: "北京"
updatedAt: "2026-10-31" // 追加新属性
};
三、核心机制:浅拷贝本质与深层嵌套对象处理
理解展开运算符的关键在于认清其浅拷贝(Shallow Copy)的本质。展开运算符只会对对象的第一层属性进行复制:
- 对于第一层的基本数据类型(如数字、字符串、布尔值),展开运算符会完整复制其数值;
- 但对于属性值为对象、数组等引用类型的数据,展开运算符仅仅复制了内存中的引用指针,而不会递归克隆深层对象。
// 含有嵌套对象的源数据
const sourceObj = {
title: "任务报告",
meta: {
author: "李四",
status: "draft"
}
};
// 使用展开运算符合并克隆
const cloneObj = { ...sourceObj };
// 1. 修改第一层属性(彼此独立,互不干扰)
cloneObj.title = "已完成报告";
console.log(sourceObj.title); // 输出仍为 "任务报告"(未被影响)
// 2. 修改深层嵌套属性(共享指针,产生连带串改)
cloneObj.meta.status = "published";
console.log(sourceObj.meta.status); // 输出变为 "published"!原对象被连带改变
如何安全处理深层嵌套对象的合并?
如果需要确保嵌套对象也是完全独立的,通常有以下两种应对方案:
-
手动进行深层展开解构:如果明确知道需要修改或合并哪一级深层属性,可在该属性下再次使用展开运算符:
const safeMerged = { ...sourceObj, meta: { ...sourceObj.meta, status: "published" // 仅在此深层创建新对象,切断指针共享 } }; -
使用深拷贝 API 或工具函数:在现代浏览器及 Node.js 环境中,可优先使用原生
structuredClone()函数进行完全深克隆后再进行合并处理。
四、边界场景与特殊类型的展开表现
在实际项目中,待合并的变量可能由于接口异步返回或入参缺失而为 null、undefined 或其他非对象类型。了解展开运算符在这些边界场景下的具体反应,能避免编写繁琐的判空防御代码。
| 展开目标类型 | 展开代码示例 | 实际合并表现与原因 |
|---|---|---|
null 或 undefined |
{ ...null, ...undefined } |
安全静默忽略,不报错,返回空对象 {}。 |
| 数字 / 布尔值 | { ...123, ...true } |
安全静默忽略,因为基本类型包装对象无自身可枚举属性。 |
| 字符串 | { ..."abc" } |
解构成以字符索引为键的对象:{ "0": "a", "1": "b", "2": "c" }。 |
| 数组 | { ...["x", "y"] } |
数组转换为以数值索引为键的对象:{ "0": "x", "1": "y" }。 |
| 原型链上的属性 | { ...childWithProto } |
只拷贝对象自身的属性,继承自原型链上的属性会被完全忽略。 |
get fullName()),展开运算符在读取时会立即触发求值,并将计算后的最终纯值写入新对象中,而不会把 Getter 函数本身拷贝过去。
五、全景特性总结与开发建议
在日常 JavaScript 编程中,使用对象展开运算符合并对象具有极高的通用性和优雅度。掌握以下几项核心要领即可从容应对各类数据组装:
- 首选方案:在无需改变原对象的前端状态管理(如 React 状态更新、Vue 数据派生)中,一律优先使用
{ ...a, ...b }。 - 关注顺序:先放基准配置,后放特异化配置;先放源对象,后写手工覆写字段。
- 防范深层共享:若数据结构多层嵌套且后续需要独立修改,务必做多层展开或配合深拷贝方法,谨防浅拷贝引起的引用串改 Bug。
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58
JS使用Array.from和Array.of创建数组的方法
2026-11-05 10:43:07








