全局嵌套const经扩展运算符克隆后的变化及防修改方法
你观察得非常准确——扩展运算符(...)对嵌套对象只能完成浅拷贝:它会为第一层属性创建新的引用,但嵌套的子对象仍然和原对象共享同一块内存空间,所以修改子属性时会同步影响原对象,就像你代码里展示的那样。
先再明确下你代码里的问题:
const foo = { a: { a1: 'a1', a2: 'a2', }, b: 'b', }; (() => { const bar1 = { ...foo }; bar1.a.a1 = 'changed'; })(); console.log(foo); // 输出 { a: { a1: 'changed', a2: 'a2' }, b: 'b' }
这里{...foo}只复制了foo的第一层属性a和b,其中a是一个对象,所以bar1.a和foo.a指向的是同一个对象,修改bar1.a.a1自然会改变原对象的对应值。
下面给你几种实用的解决方案,你可以根据自己的场景选择:
1. 手动逐层拷贝嵌套对象
如果你的对象层级不多、结构简单,手动为每个嵌套对象做浅拷贝是最直观的方式:
const foo = { a: { a1: 'a1', a2: 'a2', }, b: 'b', }; (() => { const bar1 = { ...foo, a: {...foo.a} // 单独拷贝嵌套的a对象 }; bar1.a.a1 = 'changed'; })(); console.log(foo); // 输出 { a: { a1: 'a1', a2: 'a2' }, b: 'b' },原对象不受影响
这种方法的好处是可控性强,不会有额外的性能开销,但如果对象层级很深就会很繁琐。
2. 使用JSON.parse(JSON.stringify())快速深拷贝
这是前端开发中最常用的快速深拷贝技巧,通过把对象序列化为JSON字符串再解析,生成一个完全独立的新对象:
const foo = { a: { a1: 'a1', a2: 'a2', }, b: 'b', }; (() => { const bar1 = JSON.parse(JSON.stringify(foo)); bar1.a.a1 = 'changed'; })(); console.log(foo); // 原对象保持不变
⚠️ 注意:这种方法有局限性,无法拷贝函数、Symbol、循环引用的对象,日期对象会被转为字符串,正则表达式会变成空对象。如果你的对象里没有这些特殊类型,这个方法非常方便。
3. 使用原生structuredClone()方法
现在主流浏览器和Node.js都支持原生的structuredClone(),它是专门为深拷贝设计的API,能处理大部分类型,包括循环引用、Date、RegExp、Map、Set等,比JSON方法更强大:
const foo = { a: { a1: 'a1', a2: 'a2', }, b: 'b', }; (() => { const bar1 = structuredClone(foo); bar1.a.a1 = 'changed'; })(); console.log(foo); // 原对象完全不受影响
这是目前推荐的原生深拷贝方案,无需依赖第三方库,也不用自己写复杂的递归逻辑。
4. 自定义递归深拷贝函数(或使用工具库)
如果需要处理更复杂的场景(比如拷贝函数、自定义类实例),可以自己写一个递归的深拷贝函数,或者直接使用成熟的工具库(比如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 foo = { a: { a1: 'a1', a2: 'a2', }, b: 'b', }; (() => { const bar1 = deepClone(foo); bar1.a.a1 = 'changed'; })(); console.log(foo); // 原对象不受影响
这个基础版本可以处理普通对象和数组,你可以根据需求扩展它对其他类型的支持。
内容的提问来源于stack exchange,提问作者trmaphi




