深浅拷贝

2019/11/20 javascript

深拷贝、浅拷贝的实现

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 如果判断各种数据类型

  1. typeof: 使用typeof来判断普通值与对象,注意null
  2. Object.prototype.toString.call(): 使用这个方法判断值的类型
  3. instanceof: 可以判断类型 判断是谁的实例
  4. 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的话可能会造成内存泄漏。

  1. map.set(key,value):添加键值对到映射中
  2. map.get(key):获取映射中某一个键的对应值
  3. map.delete(key):将某一键值对移除映射
  4. map.clear():清空映射中所有键值对
  5. map.entries():返回一个以二元数组(键值对)作为元素的数组
  6. map.has(key):检查映射中是否包含某一键值对
  7. map.keys():返回一个当前映射中所有键作为元素的可迭代对象
  8. map.values():返回一个当前映射中所有值作为元素的可迭代对象
  9. map.size:映射中键值对的数量

Search

    Table of Contents