首先需要明确的一点是,深拷贝和浅拷贝只针对Object和Array这样的引用数据类型。
因为基本数据类型如String, Number, Boolean等之类的数据是直接存放在栈内的,而引用数据类型存储的是该对象在栈中的引用(也就是指针),真实的数据则存放在堆里。当被引用的时候,先找到栈内的指针再通过指针找到存放在堆里的数据。
赋值
对象赋值时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。
浅拷贝
浅拷贝会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。
深拷贝
深拷贝的复制会另外创造一个一模一样的对象,新对象跟原对象不共享内存,所以操作新对象不会影响到原对象。
浅拷贝的实现方式
1.Object.assign()
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
// obj1内的a为对象,所以是引用类型,拷贝的是地址而不是数据
var obj1 = { a: {a: "aaa", b: 111} };
var obj2 = Object.assign({}, obj1);
obj2.a.a = "bbbbb";
console.log(obj1.a.a); //bbbbb
// obj3内的属性没有引用类型,则属于深拷贝
let obj3 = {a: 'aaaa'};
let obj4 = Object.assign({},obj3);
obj4.a= 'bbbbb';
console.log(obj3);//{a: "aaaa"}
2.Object.create()
直接使用var newObj = Object.create(oldObj)
var obj1 = { a: {a: "aaa", b: 111} };
var obj2 = Object.create(obj1);
obj2.a.a = "bbbbb";
console.log(obj1.a.a); //"bbbb"
3.Array.prototype.concat()
let arr1 = [1, 2, {a: 3}];
let arr2 = arr1.concat();
arr2[2].a = 4;
console.log(arr1[2]);//{a: 4}
4.Array.prototype.slice()
let arr1 = [1, 2, {a: 3}];
let arr2 = arr.slice();
arr2[1] = 4;
arr2[2].a = 4;
console.log(arr1[1]);//2
console.log(arr1[2]);//{a: 4}
五、深拷贝的实现方式
1.JSON.parse(JSON.stringify())
let arr1 = [1, 2, {a: 3}];
let arr2 = JSON.parse(JSON.stringify(arr1));
arr2[2].a = 4;
console.log(arr1[2])//3
这种方法只能实现数组或对象深拷贝,不能处理函数,因为JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,不能接受函数
2020年6月29日 今天电话面试的时候面试官问这个方法为什么不能处理函数,当时没答出来,现在来记录一下。
如果用这个方法处理函数的话会返回null所以不能处理。
2.手写递归方法
递归实现:遍历对象/数组直到里边都是基本数据类型,最后再复制
//检测数据类型的功能函数
function checkedType(target) {
return Object.prototype.toString.call(target).slice(8, -1)
}
//实现深度克隆---对象/数组
function clone(target) {
//判断拷贝的数据类型
//初始化变量result 成为最终克隆的数据
let result, targetType = checkedType(target)
if (targetType === 'Object') {
result = {}
} else if (targetType === 'Array') {
result = []
} else {
return target
}
//遍历目标数据
for (let i in target) {
//获取遍历数据结构的每一项值。
let value = target[i]
//判断目标结构里的每一值是否存在对象/数组
if (checkedType(value) === 'Object' ||
checkedType(value) === 'Array') { //对象/数组里嵌套了对象/数组
//继续遍历获取到value值
result[i] = clone(value)
} else { //获取到value值是基本的数据类型或者是函数。
result[i] = value;
}
}
return result
}