• JavaScript深浅拷贝实现


    浅拷贝

    概念

    自己创建一个新的对象,来接受你要重新赋值或引用的对象值。如果对象属性是基本数据类型的值,复制的是基本类型的值给新对象。如果属性是引用类型的值,复制的是内存中的地址,如果其中一个对象改变了这个内存中的地址,肯定会影响另一个对象。

    实现

    1. Object.assign():不会拷贝对象的继承属性,不会拷贝对象的不可枚举属性,可以拷贝Symbol类型的属性
    2. 扩展运算符
    3. concat拷贝数组
    4. 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;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    深拷贝

    对于复杂类型的值,其在堆内存中开辟了一块内存地址,并将原有的对象完全复制过来存放。

    1. JSON.stringify():对象的值中有function、undefined、symbol类型,经过序列化后键值会消失

    2. 拷贝Date类型会变成字符串

    3. 无法拷贝不可枚举的属性

    4. 无法拷贝对象的原型链

    5. 拷贝RegExp会变成空对象

    6. 对象中含有NaN、Infinity、-Infinity、JSON的序列化结果会变成null

    7. 无法拷贝对象的循环引用

    8. 手写递归

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 改进版

    改进方法

    • 针对能够遍历对象的不可枚举属性以及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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    Javascript异步编程深入浅出
    如何用Redis实现事物以及锁?
    抖音全接口API
    关于模型融合Stacking的一些改进思路
    【UE】简单的半透明+描边效果
    golang的new和make
    PinnedSectionItemDecoration - 一个强大的粘性标签库
    JavaWeb ThreadLocal 的使用
    ioDraw:与 GitHub、gitee、gitlab、OneDrive 无缝对接,绘图文件永不丢失!
    【Android - 技术期刊】第002期
  • 原文地址:https://blog.csdn.net/qq_40850839/article/details/126859603