• 【JS学习】--深拷贝与浅拷贝


    00⭐⭐(!!)深拷贝与浅拷贝

    (只谈论了 基本数据类型 和 ES5 的引用数据类型)

    (关于 ES6 及之后 新增的Symbol,Map 等数据类型还有待谈论)

    (以下仅为 原生JS 的方法,未涉及第三方库使用,第三方库lodash也有函数可实现)

    注意:

    • ⭐浅拷贝 与 深拷贝 都是需要开辟新内存地址,产生新对象
    • 但是
    • 浅拷贝 只拷贝 原始对象中的 最外部一层
    • 深拷贝 则会拷贝 原始对象中 所有的层级

    Eg:

    对于下面这个 对象

    const hotGirl = {
        name: 'Maji',
        age: 28,
        info: {
            desc: 'superHot',
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 浅拷贝:
      • 只能拷贝属性nameage拥有新地址,但是 拷贝到 info时 则会引用原始对象 中的地址
      • (!!!即改变原始对象中的info.desc的值时,浅拷贝出来的新对象的info.desc的值同样会改变),
      • 属于不彻底拷贝,所以 只有一层属性的对象 适合浅拷贝。
    • 深拷贝
      • 则可以 完全 拷贝所有层级,变成一个拥有 新地址 的 新对象。属于彻底的拷贝

    浅拷贝:只拷贝最外面一层的数据-(不彻底的拷贝)

    一 使用Object.assign(target,source01,source02,...)

    • 语法:(将source对象中的值 拷贝到 target目标对象中)

    • target 可以写成空对象

    • **注意:**若target非空 ,且source 和 target中有重复属性 ,则target中的属性会被覆写;target的值会被改变, 但是source的值不会

    • // obj2 拿到的就是 target 
      let obj2 = Object.assign(target,source)
      
      • 1
      • 2
    Eg: (得到新的对象)
    const hotGirl = {
        name: 'Maji',
        age: 28,
    };
    // target `可以写成空对象`
    let newHotGirl = Object.assign({},hotGirl)
    console.log(newHotGirl)
    // {name: 'Maji', age: 28}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Eg (重新赋值给目标对象)
    const hotGirl = {
        name: 'Maji',
        age: 28,
    };
    const pureGirl = {
        name: 'Olla',
        age: 26,
    	tall:"180cm"
    };
    
    // 让Olla 从 pureGirl 成为 hotGirl 
    // 注意这里 hotGirl 的属性被改变了,pureGirl没有
    Object.assign(hotGirl,pureGirl)
    
    // target `可以写成空对象`
    console.log(hotGirl)
    // {name: 'Olla', age: 26, tall: '180cm'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    二 使用扩展语法 (...)

    • 使用简单,不必多说
    const hotGirl = {
        name: 'Maji',
        age: 28,
    };
    let newHotGirl = { ...hotGirl }
    console.log(newHotGirl)
    //{name: 'Maji', age: 28}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    三 for…in 循环拷贝

    const hotGirl = {
        name: 'Maji',
        age: 28,
    };
    
    const newHotGirl = {};
    //  用 for in 将 obj1 的值拷贝给 obj2
    for (let key in hotGirl) {
        //字符串 赋值 就是开辟 新的内存地址,成为完全的新值
        newHotGirl[key] = hotGirl[key];
    }
    console.log(newHotGirl)
    //{name: 'Maji', age: 28}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    深拷贝: 每一层数据都会拷贝-(最彻底的拷贝)

    一 JSON方法

    • 语法:let newObj = JSON.parse(JSON.stringify(obj))

    缺陷(暂不谈论,死记)
    • 会忽略 undefined
    • 会忽略 symbol
    • 会忽略函数:JSON.stringify() 会默认移除函数。
    • JSON.stringify() 无法拷贝 Map、Set、RegExp 这些特殊数据类型。
    • 不能序列化函数 ???
    • 不能解决循环引用的对象 见[前端进阶之道-深浅拷贝](JS | 前端进阶之道 (yuchengkai.cn))

    二 ⭐⭐ JS 有原生的深拷贝 API structuredClone

    [MDN—structuredClone()](structuredClone() - Web 开发技术 | MDN (mozilla.org))

    • 结构化拷贝算法的实现,能够实现几乎对所有数据类型的深拷贝

    • 该API 较新,兼容要考虑(目前主流都支持) ,虽然有些缺点,但是不影响,放心用,以后应该会加大支持力度

    • 相比 JSON.parse()structuredClone API 的性能更好

    • 语法:

    • let hotGirl = {
          age: 1,
          info: {
              cup: 'E'
          }
      }
      let newHotGirl = structuredClone(hotGirl);
      console.log(newHotGirl)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    缺点:(死记,放心用,以后会加大支持力度)
    • 原型:无法拷贝对象的原型链。

    • 函数:无法拷贝函数。(普通函数、箭头函数、类、方法)

      • let hotGirl = {
                    age: 1,
                    info: {
                        cup: 'E'
                    },
                    f:function(){
                        console.log('hot')
                    }
                }
                let newHotGirl = structuredClone(hotGirl);
                console.log(newHotGirl)
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
    • DOM节点

    • 不可克隆:并没有支持所有类型的拷贝,比如 Error

    三 for … in 手写

    完整源码

    // 手写深拷贝
    export function deepClone(obj) {
      //判断 传入对象 为 数组 或者 对象
      var result = Array.isArray(obj) ? [] : {};
      // for in 遍历
      for (var key in obj) {
        // 判断 是否 为自身 的属性值(排除原型链干扰)
        if (obj.hasOwnProperty(key)) {
          // 判断 对象的属性值 中 存储的 数据类型 是否为对象
          if (typeof obj[key] === 'object') {
            // 有可能等于 null
            if (obj[key] === null ) {
              result[key] = null
              continue  
            }
            // 递归调用
            result[key] = deepClone(obj[key]);   //递归复制
          } 
          // 不是的话 直接 赋值 copy
          else {
            result[key] = obj[key];
          }
        }
      }
      // 返回 新的对象
      return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    补充说明

    • 2022 09 14 在if (typeof obj[key] === 'object')' 之内增加判断null 操作;修复之前(将null 拷贝成 {}的问题)
    if (typeof obj[key] === 'object') {
      // 有可能等于 null
      if (obj[key] === null ) {
        result[key] = null
        continue  
      }
      // 递归调用
      result[key] = deepClone(obj[key]);   //递归复制
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    参考文章

    30-浅拷贝和深拷贝 | 千古前端图文教程 (qianguyihao.com)

    JS | 前端进阶之道 (yuchengkai.cn)

    JS中对象深拷贝:structuredClone()_俊刚的博客-CSDN博客

    JS 深拷贝的原生终结者 structuredClone API - 掘金 (juejin.cn)

  • 相关阅读:
    把文件上传到Gitee的详细步骤
    MATLAB集合操作
    Linux centos安装SQL Server数据库,结合cpolar内网穿透实现公网访问
    使用 Spring Envers 的数据库事务审计
    【LeetCode-数组】--搜索插入位置
    NeRF OpenCV OpenGL COLMAP DeepVoxel坐标系朝向
    【无标题】
    Ps 在用鼠标滚轮缩放图片时,速度太快?
    Vue源码学习(三):<templete>渲染第二步,创建ast语法树
    ChatGPT与码农的机会
  • 原文地址:https://blog.csdn.net/m0_61486963/article/details/126439807