什么是深拷贝和浅拷贝?
浅拷贝:B复制了A,A发生变化B也跟着发生变化
深拷贝:B复制了A,A发生变化B不会跟着变化
为什么拷贝分深拷贝和浅拷贝?
基本类型和引用类型在内存中存储是有区别的,基本类型的数据是直接存储的它的值在内存栈中,而引用类型的数据存储的是它的引用地址在内存栈中,这个引用地址指向的在堆中的数据才是这个引用类型的正确数据。
一般引用类型的数据的复制只是复制它的引用地址,复制引用地址的行为就是浅拷贝。
所以引用类型的数据才存在深拷贝和浅拷贝,如对象和数组
1、如何实现深拷贝
1.1、通过JSON.parse(JSON.stringify)序列化
这是一个比较简便的方法,写法简单,如果要拷贝的对象中都是一些基本数据类型的数据可以使用这个方法
let origin = {name: '张三', age: 24}
let result = JSON.parse(JSON.stringify(origin))
但是存在一些弊端:
- 如果拷贝对象里有时间对象,时间将只是字符串的形式,而不是对象的形式
- 如果拷贝对象里有RegExp、Error对象,那得到的结果是空对象
- 如果拷贝对象里有函数、undefined,得到的结果会把函数或undefined丢失
- 如果拷贝对象里有NaN、Infinity,结果会变成null
- 只能拷贝对象的可枚举的自有属性,如果拷贝对象中的对象是有由构造函数生成的,会丢弃对象的constructor
1.2、通过自定义深拷贝方法
如果要深拷贝的内容中有一些像时间对象、正则对象、Error对象、函数、undefined、NaN、Infinity、不可枚举的自由属性、对象的constructor,就需要用下面这个方法了,不然会出现1.1中说的弊端这些问题
function deepClone(original, cache = new WeakMap()) {
if (typeof original !== 'object') return original
if (original === null) return original
if (cache.get(original)) return cache.get(original) // 防止循环引用进入死循环
if (original instanceof Date) return new Date(original)
if (original instanceof RegExp) return new RegExp(original)
// 找到所属原型上的constructor,所属原型上的constructor指向当前对象的构造函数
let cloneObj = new original.constructor()
cache.set(original, cloneObj) // 缓存拷贝的对象,用于处理循环引用的情况
for (let key in original) {
if (original.hasOwnProperty(key)) {
cloneObj[key] = util.deepClone(original[key], cache) // 递归拷贝
}
}
return cloneObj
}