JavaScript 深拷贝与浅拷贝

157 阅读2分钟

浅拷贝

  • 浅拷贝复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存

  • 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象

  • Object.assign

    • 当object只有一层的时候,是深拷贝
    var obj = { a: {a: "kobe", b: 39} };
    var initalObj = Object.assign({}, obj);
    initalObj.a.a = "wade";
    console.log(obj.a.a); //wade
    
    let obj = {
        username: 'kobe'
        };
    let obj2 = Object.assign({},obj);
    obj2.username = 'wade';
    console.log(obj);//{username: "kobe"}
    
  • Array.prototype.concat()

    • 不修改原数组,只会返回浅拷贝的新数组
    let arr = [1, 3, {
        username: 'kobe'
        }];
    let arr2=arr.concat();    
    arr2[2].username = 'wade';
    console.log(arr);
    
  • Array.prototype.slice()

    • 不修改原数组,只会返回浅拷贝的新数组
    let arr = [1, 3, {
        username: ' kobe'
        }];
    let arr3 = arr.slice();
    arr3[2].username = 'wade'
    console.log(arr);
    
  • ES6 ...

赋值

  • 赋的其实是该对象的在栈中的地址,而不是堆中的数据
let arr = [1, 2, 3, 4, 5];
// = 赋值
let newArr = arr;
newArr.push(6);
// console.log(arr, newArr)

// 基本类型不存在深浅拷贝
let a = 1;
let b = a;
b = 2;
// console.log(a)

解构赋值是是浅拷贝

  • 解构的原对象

    • 一维数组或对象 => 深拷贝

    • 多维数组或对象 => 浅拷贝

let arr2 = [[1, 2], [2, 3], [3, 4]];
let newArr2 = [...arr2]; // ES6 => 解构赋值
newArr2[0].push(666)
console.log(arr2, newArr2) 
// [[1, 2, 666], [2, 3], [3, 4]]   [[1, 2, 666], [2, 3], [3, 4]]

深拷贝

JSON.parse(JSON.stringify(obj))

  • 最快捷的深拷贝方法 80%的场景

    let obj = {
      a: { aa: 1, bb: 2 },
      b: 3,
      c: 4,
    }
    let newObj = JSON.parse(JSON.stringify(obj));
    newObj.a.aa = 666;
    
    // 方法不能拷贝
    // function => "function"
    
    console.log(obj, newObj)
    
  1. 无法处理循环引用

    • 即对象的属性之间相互引用,导致循环引用部分丢失,或者导致一个无限循环的结构,最终导致堆栈溢出
  • 不能处理函数

函数库lodash

  • _.cloneDeep用来做 Deep Copy

    var _ = require('lodash');
    var obj1 = {
        a: 1,
        b: { f: { g: 1 } },
        c: [1, 2, 3]
    };
    var obj2 = _.cloneDeep(obj1);
    console.log(obj1.b.f === obj2.b.f);
    // false
    

手写 深拷贝 → 引用数据类型(数组/对象)

function deepClone (source) {
  // 容器
  // [] => Array(基类)() => Object
  // 外层定义
  const targetObj = source.constructor === Array ? [] : {};

  for (let keys in source) {
    // 判断是不是夹心的
    if (source.hasOwnProperty(keys)) {
      // object => 对象,数组,内层定义
      if (source[keys] && typeof source[keys] === "object") {
        // targetObj[keys] = source[keys].constructor === Array ? [] : {};
        // 递归
        targetObj[keys] = deepClone(source[keys])
      } else {
        // 基本类型,直接赋值
        targetObj[keys] = source[keys];
      }
    }
  }
  return targetObj;
}

// obj => Object [] => Array  // 基类

let obj2 = {
  a: { aa: 1, bb: 2 },
  b: 3,
  c: 4,
}

let newObj222 = deepClone(obj2);
newObj222.a.bb = 6688;
console.log(obj2, newObj222)