前言
深拷贝和浅拷贝是相对于引用类型而言的,因为对于值类型的数据而言,直接用 = 赋值即可,对于引用类型的数据而言,它的名字存在栈内存中,值存在堆内存中,但是栈内存中有一个指针指向堆内存中的值,浅拷贝中复制的是地址,只要有一个变量改变了,那么最终的值都会改变,深拷贝本质上是在堆内存中开辟一个新的内存用于存放复制后的变量的值。
实现浅拷贝的方式
1. for...in 循环第一层
function shallowCopy(obj) {
let newObj = Array.isArray(obj) ? [] : {};
for (let i in obj) {
newObj[i] = obj[i];
}
return newObj;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = shallowCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 4
alert(obj2.c.d); // 4
注:使用for...in循环第一层进行浅拷贝时对象的值如果是值类型就不会改变,如果对象的值是引用类型就会改变。
2. Object.assign
var obj = {
a: 1,
b: 2
}
var obj1 = Object.assign(obj);
obj1.a = 3;
console.log(obj.a) // 3
注:使用Object.assign进行浅拷贝时,对象的值不论是值类型还是引用类型都会改变。
3. 直接用=赋值
let a=[0,1,2,3,4];
let b=a;
console.log(a===b); //[1,1,2,3,4]
a[0]=1;
console.log(a,b); //[1,1,2,3,4]
注:直接用=赋值进行浅拷贝时,对象的值不论是值类型还是引用类型都会改变。
实现深拷贝的方式
1. 采用递归去拷贝所有层级属性
function deepCopy(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === 'object') {
for (ley key in obj) {
if (obj.hasOwnProperty(key)) {
// 判断obj子元素是否为对象,如果是,递归复制
if (obj[key] && typeof obj[key] === 'object') {
objClone[key] = deepCopy(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],
b=deepCopy(a);
a[0]=2;
console.log(a,b); // [2,2,3,4] [1,2,3,4]
2. 通过JSON对象来实现深拷贝
function deepCopy(obj) {
let _obj = JSON.stringify(obj);
let newObj = JSON.parse(_obj);
return newObj;
}
注:通过JSON对象来实现深拷贝不能实现对对象中方法的深拷贝,会显示为undefined
3. 通过jQuery的extend方法实现深拷贝
let array = [1, 2, 3, 4];
let newArray = $.extend(true, [], array); // true为深拷贝,false为浅拷贝
4. lodash函数库实现深拷贝
let result = _.cloneDeep(test);
5. Reflect法
// 代理法
function deepClone(obj) {
if(!isObject(obj)) {
throw new Error('obj 不是一个对象!');
}
let isArray = Array.isArray(obj);
let cloneObj = isArray ? [...obj] : {...obj};
Reflect.ownKeys(cloneObj).forEach(key => {
cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key];
});
return cloneObj;
}
6. 手动实现深拷贝
let obj1 = {
a: 1,
b: 2
};
let obj2 = {
a: obj1.a,
b: obj1.b
};
obj2.a = 3;
console.log(obj1.a); // 1
console.log(obj2.a); // 3
7. 如果对象的value是基本类型的话,也可以用Object.assign来实现深拷贝,但是要把它赋值给一个空对象
let obj = {
a: 1,
b: 2
};
let obj1 = Oobject.assign({}, obj);
obj1.a = 3;
console.log(obj.a); //1
8. 用slice实现对数组的深拷贝
// 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝
// 当数组里面的值是引用数据类型,比如Object, Array时,属于浅拷贝
let arr1 = ["1", "2", "3"];
let arr2 = arr1.slice(0);
arr2[1] = "9";
console.log("数组的原始值:" + arr1); // ["1", "2", "3"]
console.log("数组的新值:" + arr2); // ["1", "9", "3"]
9. 用concat实现对数组的深拷贝
// 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝
let arr1 = ["1", "2", "3"];
let arr2 = arr1.concat();
arr2[1] = "9";
console.log("数组的原始值:" + arr1); // ["1", "2", "3"]
console.log("数组的新值:" + arr2); // ["1", "9", "3"]
// 当数组里面的值是引用数据类型,比如Object, Array时,属于浅拷贝
10. 直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果
function deepClone(initalObj, finalObj) {
let obj = finalObj || {};
for (let i in initalObj) {
let prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
obj[i] = prop;
}
}
return obj;
}
11. 使用扩展运算符实现深拷贝
// 当value是基本数据类型,比如String,Number,Boolean时,是可以使用拓展运算符进行深拷贝的
// 当value是引用数据类型的值,比如Object, Array时,引用类型进行深拷贝也只是拷贝了引用地址,所以属于浅拷贝
let car = {brand: "BMW", price: "380000", length: "5米"};
let car1 = {...car, price: "500000"};
console.log(car1); // { brand: "BMW", price: "500000", length: "5米" }
console.log(car); // { brand: "BMW", price: "380000", length: "5米" }