在前端开发中,我们经常需要复制对象或数组,但不同的复制方式可能会影响数据的完整性和应用的稳定性。本文将深入探讨浅拷贝(Shallow Copy)和深拷贝(Deep Copy)的区别、实现方式及适用场景。
1. 浅拷贝
1.1 什么是浅拷贝
浅拷贝指的是仅复制对象的第一层属性,而不会复制嵌套对象的引用。如果原对象的某个属性是引用类型(如对象或数组),浅拷贝后,拷贝对象的该属性仍然指向原对象中的引用地址,修改拷贝对象的嵌套属性会影响原对象。
1.2 浅拷贝的实现方式
1)Object.assign()
Object.assign()
是ES6中对象的拷贝方法,接受的第一个参数是目标对象,其余参数是源对象,用法:Object.assign(target, source_1, ···)
,该方法可以实现浅拷贝,也可以实现一维对象的深拷贝。
let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target,object2,object3);
console.log(target); // {a: 1, b: 2, c: 3}
2)扩展运算符
使用扩展运算符可以在构造字面量对象的时候,进行属性的拷贝。语法:let cloneObj = { ...obj };
let obj1 = {a:1,b:{c:1}}
let obj2 = {...obj1};
obj1.a = 2;
console.log(obj1); //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}
obj1.b.c = 2;
console.log(obj1); //{a:2,b:{c:2}}
console.log(obj2); //{a:1,b:{c:2}}
3)数组方法实现数组浅拷贝
Array.prototype.slice
- slice()方法是JavaScript数组的一个方法,这个方法可以从已有数组中返回选定的元素:用法:array.slice(start, end),该方法不会改变原始数组。
- 该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
let arr = [1,2,3,4];
console.log(arr.slice()); // [1,2,3,4]
console.log(arr.slice() === arr); //false
Array.prototype.concat
- concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
- 该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
let arr = [1,2,3,4];
console.log(arr.concat()); // [1,2,3,4]
console.log(arr.concat() === arr); //false
4)手写实现浅拷贝
function shallowCopy(object) {if (!object || typeof object !== "object") return; // 只拷贝对象// 根据 object 的类型判断是新建一个数组还是对象let newObject = Array.isArray(object) ? [] : {};// 遍历 object,并且判断是 object 的属性才拷贝for (let key in object) {if (object.hasOwnProperty(key)) {newObject[key] = object[key];}}return newObject;
}
2. 深拷贝
2.1 什么是深拷贝?
深拷贝指的是创建一个完全独立的副本,拷贝后的对象与原对象互不影响。如果原对象包含嵌套对象,深拷贝会递归复制所有层级的内容,而不是复制引用。
2.2 深拷贝的实现方式
1)JSON方法
- JSON.parse(JSON.stringify(obj))是目前比较常用的深拷贝方法之一,它的原理就是利用 JSON.stringify 将 js对象序列化(JSON字符串),再使用 JSON.parse来反序列化(还原)js对象。
- 这个方法可以简单粗暴的实现深拷贝,但是还存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过 JSON.stringify()进行处理之后,都会消失。
let obj1 = { a: 0,b: {c: 0}};
let obj2 = JSON.parse(JSON.stringify(obj1));
obj1.a = 1;
obj1.b.c = 1;
console.log(obj1); // {a: 1, b: {c: 1}}
console.log(obj2); // {a: 0, b: {c: 0}}
2)手写实现深拷贝
// 深拷贝的实现
function deepCopy(object) {if (!object || typeof object !== "object") return;let newObject = Array.isArray(object) ? [] : {};for (let key in object) {if (object.hasOwnProperty(key)) {newObject[key] =typeof object[key] === "object" ? deepCopy(object[key]) : object[key];}}return newObject;
}
3. 浅拷贝 vs 深拷贝
特点 | 适用场景 | |
---|---|---|
浅拷贝 | 仅复制第一层,引用类型仍然共享 | 适用于对象层级较浅、且不担心数据共享的情况 |
深拷贝 | 递归复制所有属性,生成全新对象 | 适用于数据完全独立、对象层级较深的情况 |