学习笔记:深拷贝与浅拷贝

88 阅读2分钟

浅拷贝

浅拷贝是指创建一个新对象,其中复制的是原对象的第一层属性值。对于原对象的基本数据类型,浅拷贝会直接复制它们的值;而对于引用类型,浅拷贝只复制引用,即新对象的属性和原对象的属性指向同一个内存地址。这样,如果修改引用类型的内部属性,原对象和拷贝对象都会受到影响。

实现浅拷贝的方法
  • Object.assign()   静态方法将一个或者多个源对象中所有可枚举的自有属性复制到目标对象,并返回修改后的目标对象,实际上对每个源对象执行的是浅复制。

  • ...扩展运算符   

  • 数组方法实现数组浅拷贝 如Array.prototype.slice、Array.prototype.concat

  • 手写实现浅拷贝

function copyShallow(obj) { 
    let newObj = Array.isArray(obj) ? [] : {}; 
    for(let key in obj) { 
    if(obj.hasOwnProporty(key)) { 
        newObj[key] = obj[key]; 
    } } 
    return newObj; 
}

深拷贝

深拷贝则是递归地复制对象的所有层级,包括嵌套的引用类型。深拷贝创建的是一个完全独立的新对象,原对象与拷贝对象之间没有共享的内存区域。因此,修改深拷贝对象中的任何属性,都不会影响原对象。

实现深拷贝的方法
  • JSON.stringify()   利用 JSON.stringify() 将对象转换为 JSON 字符串,再用 JSON.parse() 将 JSON 字符串解析为新的对象。但无法拷贝BigInt(报错)、无法处理对象的循环引用(报错)、无法拷贝Symbol、function、undefined。

  • 函数库lodash的_.cloneDeep方法  

  • structuredClone(官方深拷贝API)   可以处理循环引用,但不能拷贝 Symbol 和 function。 截至24年7月,structuredClone 在以下环境中受支持:

桌面浏览器:

-   Chrome 98及以上
-   Firefox 94及以上
-   Safari 15.4及以上
-   Edge 98及以上

移动浏览器:

-   Chrome for Android 98及以上
-   Firefox for Android 94及以上
-   Safari on iOS 15.4及以上
-   Samsung Internet 16.0及以上
  • 手写实现深拷贝   
function deepCopy(obj, hash = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  if (hash.has(obj)) {
    return hash.get(obj);
  }

  const result = Array.isArray(obj) ? [] : {};
  hash.set(obj, result);

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      console.log(obj[key] === original);
      result[key] = deepCopy(obj[key], hash);
    }
  }
  return result;
}

核心逻辑

  • 检查是否是对象:函数首先判断是否是对象或 null,如果不是,直接返回(处理基本类型)。

  • 利用 WeakMap 记录已拷贝的对象:每当处理一个对象时,先检查 WeakMap 中是否已存在该对象。如果存在,说明遇到了循环引用,直接返回之前记录的拷贝。

  • 递归拷贝对象的属性:如果没有循环引用,继续递归拷贝对象的每个属性,并在 WeakMap 中记录当前对象的拷贝,防止后续递归中再次处理到它。