在学习深拷贝之前,我们要先搞明白什么是深拷贝?
在JS中,数据类型分为基本数据类型和引用数据类型两种,对于基本数据类型来说,它的值直接存储在栈内存中,而对于引用类型来说,它在栈内存中仅仅存储了一个引用,而真正的数据存储在堆内存中。
1、当我们对数据进行操作的时候,会发生两种情况
基本数据类型
var a = 3;
var b = a;
b = 5;
console.log(a); // 3
console.log(b); // 5
可以看到的是对于基本类型来说,我们将一个基本类型的值赋予 a 变量,接着将 a 的值赋予变量 b ;然后我们修改 b ;可以看到 b 被修改了,而 a 的值没有被修改,两个变量都使用的是独立的数据;
引用数据类型
var obj1 = {
a: 1,
b: 2,
c: 3
}
var obj2 = obj1;
obj2.a = 5;
console.log(obj1.a); // 5
console.log(obj2.a); // 5
可以看到的是,两个对象的值全部被修改了
对象是引用类型的值,对于引用类型来说,我们将 obj1 赋予 obj2 的时候,我们其实仅仅只是将 obj1 存储在栈堆中的的引用赋予了 obj2 ,而两个对象此时指向的是在堆内存中的同一个数据,所以当我们修改任意一个值的时候,修改的都是堆内存中的数据,而不是引用,所以只要修改了,同样引用的对象的值也自然而然的发生了改变
深拷贝
深拷贝作用在引用类型上!例如:Object,Array
深拷贝不会拷贝引用类型的引用,而是将引用类型的值全部拷贝一份,形成一个新的引用类型,这样就不会发生引用错乱的问题,使得我们可以多次使用同样的数据,而不用担心数据之间会起冲突。
深拷贝的实现
1、使用JSON.stringify()以及JSON.parse()
先转化为字符串,在转换回来
var obj1 = {
a: 1,
b: 2,
c: 3
}
var objString = JSON.stringify(obj1);
var obj2 = JSON.parse(objString);
obj2.a = 5;
console.log(obj1.a); // 1
console.log(obj2.a); // 5
可以看到没有发生引用问题,修改obj2的数据,并不会对obj1造成任何影响
但是使用JSON.stringify()以及JSON.parse()它是不可以拷贝 undefined , function, RegExp 等等类型的
2、使用递归
function copyObj(obj) {
if (obj === null || obj === undefined || !(obj.constructor === Object) && !(obj.constructor === Array)) {
// 判断 如果传入的参数不是对象和数组
return obj;
// 直接返回
};
let obj2 = {};
// 定义一个对象
if (Array.isArray(obj)) obj2 = [];
// 如果传进来的是个数组 就变更为数组
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
// 判断拷贝自身的属性,而不拷贝原型上的
obj2[i] = copyObj(obj[i]);
// 就拷贝里面的每一项
}
};
return obj2;
// 返回 obj2拷贝完的数据
}
3、使用Object.assign(target, source)
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
语法:Object.assign(target, …sources)
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }
注意:只能拷贝单层数据,但是如果对象的属性对应的是其它的引用类型的话,还是只拷贝了引用,修改的话还是会有问题