首先,JS的浅拷贝和深拷贝只是针对于引用数据类型而言
- 基本数据类型:Boolean、Number、String、undefined、Null、Symbol (ES6新增,表示独一无二的值)
- 引用数据类型:Object、Array、Function
浅拷贝:
概念:浅拷贝只是复制指向某个对象的指针,而不复制对象本身,新旧对象其实还是同一个对象。修改时原对象也会受到影响。
深拷贝:
概念:深拷贝就是在拷贝数据的时候,将数据的所有引用结构都拷贝一份。简单的说就是,在内存中存在两个数据结构完全相同又相互独立的数据,将引用型类型进行复制,而不是只复制其引用关系。修改时原对象不再受到任何影响。
看一个非常典型的例子:
// 基本数据类型赋值
var a = 'aaa';
var b = a;
console.log(a); // 'aaa'
console.log(b); // 'aaa'
b = 'bbb';
console.log(a); // 'aaa'
console.log(b); // 'bbb'
// 引用数据类型赋值
var a = { name: '张三'};
var b = a;
console.log(a); // {name: "张三"}
console.log(b); // {name: "张三"}
b.name = '李四';
b.age = 18;
console.log(a); // {name: "李四", age: 18}
console.log(b); // {name: "李四", age: 18}
下面的是浅拷贝,上面的可以“理解”为深拷贝。
浅拷贝方法
- 利用 = 赋值操作符实现浅拷贝。
- 使用 slice、concat是数组的浅拷贝。
- 对象浅拷贝 - Object.assign()。
- 对象浅拷贝 - 扩展运算符
深拷贝方法
-
利用 JSON 对象中的 parse 和 stringify—如果对象某个属性值是函数的话,这个函数的值不能被深拷贝
-
利用递归来实现每一层都重新创建对象并赋值。
-
lodash
1.对于JSON.parse( JSON.stringify() ) 序列化和反序列:
先将需要拷贝的对象进行JSON字符串化,然后再pase解析出来,赋给另一个变量,实现深拷贝。
2.对于Object.assign(target, source1, source2)
es6新增的方法,可用于对象合并,将源对象的所有可枚举属性,复制到目标对象上。
3.迭代递归方法
function deepCopy(data) {
if(typeof data !== 'object' || data === null){
throw new TypeError('传入参数不是对象')
}
let newData = {};
const dataKeys = Object.keys(data);
dataKeys.forEach(value => {
const currentDataValue = data[value];
// 基本数据类型的值和函数直接赋值拷贝
if (typeof currentDataValue !== "object" || currentDataValue === null) {
newData[value] = currentDataValue;
} else if (Array.isArray(currentDataValue)) {
// 实现数组的深拷贝
newData[value] = [...currentDataValue];
} else if (currentDataValue instanceof Set) {
// 实现set数据的深拷贝
newData[value] = new Set([...currentDataValue]);
} else if (currentDataValue instanceof Map) {
// 实现map数据的深拷贝
newData[value] = new Map([...currentDataValue]);
} else {
// 普通对象则递归赋值
newData[value] = deepCopy(currentDataValue);
}
});
return newData;
}
(解决闭环问题)
function deepCopy(data, hash = new WeakMap()) {
if(typeof data !== 'object' || data === null){
throw new TypeError('传入参数不是对象')
}
// 判断传入的待拷贝对象的引用是否存在于hash中
if(hash.has(data)) {
return hash.get(data)
}
let newData = {};
const dataKeys = Object.keys(data);
dataKeys.forEach(value => {
const currentDataValue = data[value];
// 基本数据类型的值和函数直接赋值拷贝
if (typeof currentDataValue !== "object" || currentDataValue === null) {
newData[value] = currentDataValue;
} else if (Array.isArray(currentDataValue)) {
// 实现数组的深拷贝
newData[value] = [...currentDataValue];
} else if (currentDataValue instanceof Set) {
// 实现set数据的深拷贝
newData[value] = new Set([...currentDataValue]);
} else if (currentDataValue instanceof Map) {
// 实现map数据的深拷贝
newData[value] = new Map([...currentDataValue]);
} else {
// 将这个待拷贝对象的引用存于hash中
hash.set(data,data)
// 普通对象则递归赋值
newData[value] = deepCopy(currentDataValue, hash);
}
});
return newData;
}