浅拷贝
概念
自己创建一个新的对象,来接受你要重新赋值或引用的对象值。如果对象属性是基本数据类型的值,复制的是基本类型的值给新对象。如果属性是引用类型的值,复制的是内存中的地址,如果其中一个对象改变了这个内存中的地址,肯定会影响另一个对象。
实现
- Object.assign():不会拷贝对象的继承属性,不会拷贝对象的不可枚举属性,可以拷贝Symbol类型的属性
- 扩展运算符
- concat拷贝数组
- slice拷贝数组
浅拷贝最终实现:
function shallowClone(target){
if(typeof target === 'object' && target !== null){
const cloneTarget = Array.isArray(target) ? [] : {};
for(let prop in target){
if(target.hasOwnProperty(prop)){
cloneTarget[prop] = target[prop];
}
}
return cloneTarget;
} else {
return target;
}
}
深拷贝
对于复杂类型的值,其在堆内存中开辟了一块内存地址,并将原有的对象完全复制过来存放。
-
JSON.stringify():对象的值中有function、undefined、symbol类型,经过序列化后键值会消失
-
拷贝Date类型会变成字符串
-
无法拷贝不可枚举的属性
-
无法拷贝对象的原型链
-
拷贝RegExp会变成空对象
-
对象中含有NaN、Infinity、-Infinity、JSON的序列化结果会变成null
-
无法拷贝对象的循环引用
-
手写递归
function deepClone(obj){
let cloneObj = {};
for(let key in obj){
if(typeof obj[key] === 'object'){
cloneObj[key] = deepClone(obj[key]);
} else {
cloneObj[key] = obj[key];
}
}
return cloneObj;
}
- 改进版
改进方法
- 针对能够遍历对象的不可枚举属性以及Symbol类型,我们可以使用Reflect.ownKeys方法
- 当参数为Date、RexExp类型,则直接生成一个新的示例返回
- 利用Object的getOwnPropertyDescriptors()方法获取对象的所有属性,以及对应属性的特性,顺便结合Object.create()方法创建一个新对象,并继承传入原对象的原型链
- 利用weakMap类型作为hash表,因为weakMap类型是弱引用类型,可以有效防止内存泄漏,作为检测循环引用很有帮助,如果存在循环则引用直接返回weakMap存储的值
深拷贝最终实现:
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null);
function deepClone(obj, hash = new WeakMap()){
if(obj.constructor === Date){
return new Date(obj);
}
if(obj.constructor === RegExp){
return new RegExp(obj);
}
if(hash.has(obj)){
return hash.get(obj);
}
let allDesc = Object.getOwnPropertyDescriptors(obj);
let cloneObj = Object.create(Object.getPrototypeOf(obj),allDesc);
hash.set(obj, cloneObj);
for(let key of Reflect.ownKeys(obj)){
cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key];
}
return cloneObj;
}