展开运算符 {...obj} 实现对象拷贝时,仅修改第一层基础类型属性(如字符串、数字、布尔值、null、undefined)是安全的;若属性为数组、对象、Date、RegExp 或函数等引用类型,则修改会相互影响。

展开运算符实现对象拷贝时,浅层属性修改互不影响,前提是只改第一层基础类型属性(如字符串、数字、布尔值),且不触碰任何嵌套对象或数组。
哪些浅层修改是安全的
展开运算符 {...obj} 会为对象创建一个新内存地址,同时把所有第一层可枚举属性的值复制过去:
- 基础类型(string / number / boolean / null / undefined)被真正复制,修改副本不会影响原对象
- Symbol 键和不可枚举属性不会被复制,所以也不涉及影响问题
- 如果原对象只有这些简单字段,比如
{name: "张三", age: 25, isActive: true},那用{...obj}拷贝后改任意字段都完全隔离
哪些看似“浅层”实则危险
容易误判的是那些“看起来在第一层”,但实际是引用类型的值:
- 数组(
tags: ["js", "react"])——副本里的tags和原对象指向同一个数组,copy.tags.push("ts")会同步改原对象 - 日期对象(
createdAt: new Date())、正则(pattern: /abc/)、函数(handler: () => {})——都是引用,共享内存 - 普通对象(
meta: { version: 1 })——copy.meta.version = 2会改到原对象的meta.version
怎么确认某属性改起来是否安全
判断依据不是“它写在对象第一行”,而是它的 数据类型:
- 用
typeof value === "object"且value !== null且!Array.isArray(value)→ 很可能是普通对象,不安全 - 用
Array.isArray(value)→ 数组,不安全 - 用
value instanceof Date || value instanceof RegExp || typeof value === "function"→ 全都不安全 - 其余情况(string/number/boolean/null/undefined/Symbol)→ 安全
想改嵌套字段又不污染原对象?得手动展开对应层
如果必须更新像 user.profile.name 这样的路径,不能靠一次展开,而要逐层解构:
- 改 profile.name:
{...user, profile: {...user.profile, name: "李四"}} - 再改 profile.address.city:
{...user, profile: {...user.profile, address: {...user.profile.address, city: "杭州"}}} - 三层以上嵌套就明显变复杂,此时建议直接换深拷贝方案,比如
structuredClone(user)(现代浏览器支持)或JSON.parse(JSON.stringify(user))(仅限纯数据)

















