一、浅拷贝和深拷贝
浅拷贝:创建一个新对象来接受将要复制的对象值,如果属性值是基本类型则复制一份新的值,如果是引用类型,则保存的是内存的引用值,存在值共享的现象。
深拷贝:创建一个新对象来接受简要复制的对象值,如果属性值是基本类型则复制一份新的值,如果是引用类型,则开辟一个新的内存空间进行保存,两个引用数据相互独立,互不影响。
二、浅拷贝的api
1、Object.assign,用于合并对象,属于浅拷贝。
let clonedObj = Object.assign({},origin)
2、拓展运算符展开
let clonedObj = {…origin}
拓展运算符要更简洁
三、深拷贝
1、JSON.Stringify()
缺点:函数、undefined、symbol数据类型经过JSON.Stringify后,键值对会消失
2、基础版:递归赋值
const isComplexDataType = (obj) => (typeof origin === 'object' && origin !== null);
function deepClone(origin){
let newObj;
if(isComplexDataType(origin)){
newObj = Array.isArray(origin) ? [] : {}
for(let key in origin){
if(origin.hasOwnProperty(key)){
if(isComplexDataType(origin[key])){
newObj[key] = Array.isArray(origin[key]) ? [] : {}
newObj[key] = deepClone(origin[key])
}{
newObj[key] = origin[key]
}
}
}
return newObj
}else{
return origin
}
}
缺点:
1、无法正确克隆date、RegExp等类型对象
2、不能克隆不可枚举和synbol类型属性 : 通过Object.create传入owenPropertyDescriptors
3、无法继承原型链: Object.create
4、无法解决循环引用造成的栈溢出问题 通过map来保存拷贝过的值,进一步用weakMap(对key值是弱引用,垃圾回收机制会释放未被引用的key)来自动释放内存
3、解决方案
(1)克隆参数有两个,克隆对象,克隆记录(weakMap,只接受引用值作为箭名,对键值为弱引用,停止引用后,会在垃圾回收机制执行时回收)
(2)判断是否为内置对象实例,regxp,date对象
(3)在克隆记录中查询是否有缓存,有的话,返回缓存值
(4)获取对象原型(解决原型继承问题),属性描述符(解决了getter和setter无法拷贝的问题)
(5)用Object.create创建新对象
(6)遍历新对象自身属性,进行递归拷贝
(7)将此次拷贝记录下来
(8)返回拷贝对象
const isComplexDataType = (obj) => (typeof obj === 'object' && obj !== null);
function deepClone(origin , hash = new WeakMap()){
if(isComplectDataType(obj)){
//判断date、RegExp类型对象
if(instanceof Date){
return new Date(obj)
}
if(instanceof Regxp){
return new Regxp(obj)
}
// 判断hash表中是否已有缓存,避免自身引用导致的循环递归
if(hash.get(obj)) return hash.get(obj)
//获取属性描述符,然后创建新对象
let descriptors = Object.getOwnPropertyDescriptors(obj);
let newObj = Object.create(Object.getPrototypeOf(obj) ,descriptors);//可以创建对象和数组
hash.set(obj, newObj);
for(let key of Reflect.ownKeys(obj)){
newObj[key] = isComplexDataType(origin[key]) ? deepClone(origin[key]) : origin[key]
}
return newObj
}else{
return obj
}
}