对象中Spread语法的工作原理:为何嵌套属性修改会影响原对象?
为什么Spread语法修改嵌套属性会影响原对象?如何避免?
这是JavaScript对象拷贝里非常典型的一个误区,我来给你把逻辑拆解明白:
问题本质:Spread语法是浅拷贝
你第一个例子里的human只有基础类型属性(数字age),Spread语法会直接把这些基础类型的值复制到新对象john中——这时候两个对象的age是完全独立的内存空间,所以修改john.age自然不会影响原对象的human.age。
但第二个例子里的cars是引用类型(数组),Spread语法只会复制这个数组在内存中的引用地址,而不是数组本身。也就是说,human.cars和john.cars指向的是同一个底层数组。不管你通过哪个对象去修改数组元素,本质上都是在操作同一个内存里的数组,所以两个对象的cars都会同步变化。
如何避免这种不一致行为?
要彻底让新对象和原对象完全独立,我们需要做深拷贝——也就是把嵌套的引用类型也完整复制一份,而不是只复制引用。这里有几种常用方案:
1. 手动嵌套Spread(适合简单嵌套结构)
如果你的对象嵌套层级不多,可以手动对引用类型也使用Spread语法,复制其本身而不是引用:
const human = { age: 20, cars: ["toyota", "honda"] }; const john = { ...human, cars: [...human.cars] // 单独复制数组,生成新的数组实例 }; john.cars[1] = "camero"; console.log(human.cars); // ["toyota", "honda"] console.log(john.cars); // ["toyota", "camero"]
2. 使用JSON.parse(JSON.stringify())(通用轻量方案)
这是最常用的通用深拷贝方法,适合大多数普通对象(注意:无法拷贝函数、Symbol、循环引用等特殊类型):
const human = { age: 20, cars: ["toyota", "honda"] }; const john = JSON.parse(JSON.stringify(human)); john.cars[1] = "camero"; console.log(human.cars); // ["toyota", "honda"] console.log(john.cars); // ["toyota", "camero"]
3. 自定义递归深拷贝函数(处理特殊场景)
如果需要支持函数、循环引用等特殊类型,可以自己实现一个递归的深拷贝函数,或者使用成熟工具库(比如Lodash的_.cloneDeep):
function deepClone(obj) { // 基础类型直接返回 if (obj === null || typeof obj !== 'object') return obj; // 处理数组 if (Array.isArray(obj)) { return obj.map(item => deepClone(item)); } // 处理普通对象 const clonedObj = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { clonedObj[key] = deepClone(obj[key]); } } return clonedObj; } const human = { age: 20, cars: ["toyota", "honda"] }; const john = deepClone(human); john.cars[1] = "camero"; console.log(human.cars); // ["toyota", "honda"] console.log(john.cars); // ["toyota", "camero"]
总结一下:Spread语法只做浅拷贝,对基础类型属性有效,但引用类型只会复制内存引用;要彻底隔离原对象和新对象,必须使用深拷贝方案。
内容的提问来源于stack exchange,提问作者Isaac
相关产品推荐
相关产品推荐

