深拷贝
深拷贝是将一个对象从内存中完整的拷贝一份出来。从堆内存中开辟一个新的区域存放新的对象(新旧对象不共享同一块内存),且修改新对象不会影响到原来的对象。(深拷贝是在堆内存申请新的空间来存储数据,避免指针悬挂)
实现方式如下
JSON.parse(JSON.stringify())
注意: 上面的方法虽然可以实现数组或对象的深拷贝,但是不能处理函数和正则,,经过上面的拷贝后得到的正则就不再是正则(变为空对象),得到的函数也不再是函数(变为null)。
递归方法实现深度克隆:遍历对象、数组直到里面是基本数据类型,然后再去复制,就是深度拷贝。
javascript
function deepClone(obj) {
if (typeof obj !== 'object' || obj == null) {
return obj
}
let result
if (obj instanceof Array) {
result = []
} else {
result = {}
}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key])
}
}
return result
}
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj; //如果是null或者是undefined 我就不进行拷贝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj); //如果可能是对象或者普通的值如果是函数的话就不需要拷贝
if (typeof obj !== "object") return obj //是对象的话就要深拷贝
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
//找到的是所属类型上的constructor,而原型上的constructor指向的是当前的类本身
hash.set(obj, cloneObj);
hash.set(obj, cloneObj)
for (let key in obj) {
if (obj.hasOwnProperty('key')) {
cloneObj[key] = deepClone(obj[key], hash)
}
}
return cloneObj
}
浅拷贝
浅拷贝是创建一个新对象,这个对象有着原始对象的属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存的地址。所以其中一个对象改变了地址,就会影响到另一个对象。
实现方式如下
1、Object.assign(); 可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,最后返回目标对象。
javascript
let obj1 = {
person: {
name: '张三',
age: 21
},
sports: 'basketball'
}
let obj2 = Object.assign({}, obj1)
obj2.person.name = "艺青桑";
obj2.sports = "football";
console.log(obj1) // {person: {name: '艺青桑', age:21},sports: "basketball"}
console.log(obj2); // {person: {name: '艺青桑', age:21}, sports: 'football'}
2、展开运算符 是es6的特性,它提供了一种非常方便的方式来进行浅拷贝,与object.assign()的功能相同
javascript
let obj1 = { name: '文博', address: { x: 100, y: 100 } }
let obj2 = { ...obj1 };
obj1.address.x = 200;
obj1.name = '嘉兴';
console.log(obj2);//{ name: '文博', address: { x: 200, y: 100 }}
console.log(obj1);//{ name: '嘉兴', address: { x: 200, y: 100 }}
3、Array.prototype.concat();
javascript
let arr=[1,3,{username:'贺丽'}]
let arr2=arr.concat();
arr[2].username='亚楠';
console.log(arr) //[1,3,{username:'亚楠'}]
4、 Array.prototype.slice()
javascript
let arr = [1, 3, { username: '再兴' }]
let arr3 = arr.slice();
arr3[2].username = '艺青';
console.log(arr) // [1,3,{username:'艺青'}]
赋值与深浅拷贝的区别
|-----|--------------------------------------------------------------------------------------------------------|--------------------------------------------------|---------------------------------------------------------|
| | 赋值 | 深拷贝 | 浅拷贝 |
| | 当我们把一个对象赋值给一个新的变量时,赋的其实是给该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论那个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。 | 从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。 | 重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。 |
| | 和原数据是否指向同一对象 | 第一层数据为基本数据类型且修改基本类型数据时 | 原数据中包含子对象且修改子对象时 |
| 赋值 | 是 | 改变会使原数据一起改变 | 改变会使原数据一起改变 |
| 深拷贝 | 否 | 改变不会使原数据一起改变 | 改变不会使原数据一起改变 |
| 浅拷贝 | 否 | 改变不会使原数据一起改变 | 改变会使原数据一起改变 |
文章中提到的栈内存和堆内存,有不懂得可以参观下面的地址