深拷贝、浅拷贝的实现
1、浅拷贝
浅拷贝拷贝的是引用。 使用for in || Object.assign()
实现。
2、深拷贝
拷贝出来的结果和以前没关系叫深拷贝。 深拷贝的实现思路主要是递归,不过需要注意普通值、函数、null、date、undefined、RegExp的问题。
3、Object.assign()
MDN: Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标对象的[[Set]],所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。 针对深拷贝,需要使用其他办法,因为 Object.assign()拷贝的是属性值。
如果是普通单层对象,是深拷贝,如果是多层对象,该函数拷贝的还是引用,因为该方法拷贝的是属性值。
4、JSON.stringify & JSON.parse
使用JSON.stringify & JSON.parse
可以实现深拷贝,但是只能拷贝可以通过json序列化的属性值,比如function
等就会被忽略。
5、实现一个深拷贝
实现深拷贝还是用递归的方法,但是需要注意各种数据类型的边界。
5.1 如果判断各种数据类型
- typeof: 使用typeof来判断普通值与对象,注意null
- Object.prototype.toString.call(): 使用这个方法判断值的类型
- instanceof: 可以判断类型 判断是谁的实例
- constructor: 判断构造函数
通过以上几种可以判断出一个数据到底是什么类型。
5.2 实现一个深拷贝
const deepClone = (value ,hash = new WeakMap) => {
if(value == null) return value; // 如果为null或undefined
if(typeof value !== 'object') return value; // 如果不是一个对象
if(value instanceof RegExp) return new RegExp(value); // 如果是正则表达式
if(value instanceof Date) return new Date(value); // 如果是日期对象
// 拷贝的人可能是一个对象 或者是一个数组 (循环) for in
let instance = new value.constructor; // 根据当前属性构造一个新的实例
if(hash.has(value)){ // 先去hash中查看一下是否存在过 ,如果存在就把以前拷贝的返回去
return hash.get(value); // 返回已经拷贝的结果
}
hash.set(value,instance); // 没放过就放进去
// 用一个对象来记忆
for(let key in value){
// 过滤掉原型链上的属性
if(value.hasOwnProperty(key)){
// 将hash 继续向下传递 保证这次拷贝能拿到以前拷贝的结果
// 产生的就是一个新的拷贝后的结果
instance[key] = deepClone(value[key],hash);
}
}
return instance
};
5.3 WeakMap
WeakMap 和 Map使用方法一致,但是WeakMap是弱引用,只要其中任意一个引用被释放,该键值对就会被删除。因为V8引擎的垃圾回收机制是标记清除法/引用计数
,使用Map的话可能会造成内存泄漏。
- map.set(key,value):添加键值对到映射中
- map.get(key):获取映射中某一个键的对应值
- map.delete(key):将某一键值对移除映射
- map.clear():清空映射中所有键值对
- map.entries():返回一个以二元数组(键值对)作为元素的数组
- map.has(key):检查映射中是否包含某一键值对
- map.keys():返回一个当前映射中所有键作为元素的可迭代对象
- map.values():返回一个当前映射中所有值作为元素的可迭代对象
- map.size:映射中键值对的数量