深拷贝
前提
- 基本数据类型有:number,string,boolean,null,undefined,symbol,BigInt;
- 引用数据类型:object,array,function等;
- 基本数据类型存储在栈内存中,当复制时,栈内存会新开辟一个内存,即对于基本类型的数据,是拷贝值,不会拷贝引用;
- 对于引用类型的数据,它们的名字存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值,当复制时,其实复制的是引用地址,而非堆里面的值,所以我们要在堆内存中也开辟一个新的内存存放拷贝值,(调用他们的构造函数,创建一个新的引用,之后遍历原引用类型中的所有属性,递归拷贝);
- 在写之前先定义一个引用数据类型,以下的测试都可以用这个数据
const obj = {
name:'张三',
info: {
age: 20,
sex: 'male',
fancy:['唱歌','跳舞']
}
}
JSON 方法
- 不支持值为undefined、函数和循环引用的情况;
const cloneObj = JSON.parse(JSON.stringify(obj))
递归法拷贝
function deepCopy(obj, cache = new WeakMap()) {
if(obj === null || typeof obj !== 'object') return obj
if(obj instanceof Date) return new Date(obj)
if(obj instanceof RegExp) return new RegExp(obj)
if(cache.has(obj)) return cache.get(obj)
let newObj = new obj.constructor()
//缓存对象,记录已经拷贝过的引用类型属性,值为拷贝后的新的引用,在发现循环引用之后,直接使用缓存中的结果,而不是再次拷贝,避免循环引用的情况
cache.set(obj, newObj)
for(let key in obj) {
//hasOwnProperty() 方法是 Object 的原型方法(也称实例方法),它定义在 Object.prototype 对象之上,所有 Object 的实例对象都会继承 hasOwnProperty() 方法;是用来检测属性是否为对象的自有属性,如果是,返回true,否者false;
//hasOwnProperty() 只会检查对象的自有属性,对象原形上的属性其不会检测;但是对于原型对象本身来说,这些原型上的属性又是原型对象的自有属性,所以原形对象也可以使用hasOwnProperty()检测自己的自有属性;
if(obj.hasOwnProperty(key)) {
newObj[key] = deepCopy(obj[key], cache)
}
}
return newObj
}
function deepCopy(obj) {
if(obj === null || typeof obj !== 'object') return obj
if(Object.prototype.toString.call(obj) === "[Object Date]") return new Date(obj)
if(Object.prototype.toString.call(obj) === "[Object RegExp]") return new RegExp(obj)
if(Object.prototype.toString.call(obj) === "[Object Undefined]") return new Error(obj)
let newObj = Array.isArray(obj) ? [] : {}
//for-in只是获取数组的索引;而for-of会获取数组的值;
//for-in会遍历对象的整个原型链,性能差;而for-of只遍历当前对象,不会遍历原型链;
//对于数组的遍历,for-in会返回数组中所有可枚举的属性(包括原型链上可枚举的属性);for-of只返回数组的下标对应的属性值;
//for-of适用遍历数组/字符串/map/set等有迭代器对象的集合,但是不能遍历普通对象(obj is not iterable).
for(let key in obj) {
if(typeof obj[key] === 'object'){
newObj[key] = deepCopy(obj[key])
}else{
newObj[key] = obj[key]
}
}
return newObj
}
function deepCopy(obj) {
if(obj === null || typeof obj !== 'object') return obj
let cache = null
if(!deepCopy.cache) deepCopy.cache = new WeakMap()
cache = deepCopy.cache
if(cache.has(obj)) return cache.get(obj)
//Set 和 Map 类型的数据,我们需要拿出来它们其中所有的数据,再递归的拷贝,因为如果这里的数据有引用类型的,使用 Set、Map 构造函数,并传递原数据,仍然是浅拷贝;
if(obj instanceof Set) {
const temp = new Set()
cache.set(obj, temp)
obj.forEach(item => {
temp.add(deepCopy(item))
})
return temp
}else if(obj instanceof Map) {
const temp = new Map()
cache.set(obj, temp)
obj.forEach(item => {
temp.set(key, deepCopy(item))
})
return temp
}else if(obj instanceof RegExp) {
return new RegExp(obj)
}else if(obj instanceof Date) {
return new Date(obj)
}else{
const temp = new obj.constructor()
cache.set(obj, temp)
for(let key in obj) {
temp[key] = deepCopy(obj[key])
}
return temp
}
}