Object对象的浅拷贝与深拷贝方法详解

759 阅读6分钟

关注公众号: 微信搜索 前端工具人 ; 收货更多的干货

对象的深拷贝与浅拷贝的区别如下:

  • 浅拷贝:仅仅复制对象的引用,而不是对象本身; 修改值相互影响;
  • 深拷贝:把复制的对象所引用的全部对象都复制一遍; 修改值不会相互影响。

一、浅拷贝

  • Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。 它将返回目标对象 (简单粗暴明了 推荐首选)
  • 详情直戳 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
function test() {
    'use strict';

    let obj1 = { a: 0 , b: { c: 0}};
    let obj2 = Object.assign({}, obj1);
    console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
  
    obj1.a = 1;
    console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
    console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
  
    obj2.a = 2;
    console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
    console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}}
  
    obj2.b.c = 3;
    console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
    console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
  
    // Deep Clone 
    obj1 = { a: 0 , b: { c: 0}};
    let obj3 = JSON.parse(JSON.stringify(obj1));
    obj1.a = 4;
    obj1.b.c = 4;
    console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}}

    // 拷贝单个对象 
    var obj = { a: 1 };
    var copy = Object.assign({}, obj);
    console.log(copy); // { a: 1 }  

    // 合并多个对象
    let o1 = { a: 1 };
    let o2 = { b: 2 };
    let o3 = { c: 3 };

    let obj = Object.assign(o1, o2, o3);
    console.log(obj); // { a: 1, b: 2, c: 3 }
    console.log(o1);  // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变
}

// 需要注意的是浅拷贝的话当自身或者目标对象改变两者皆会改变  (希望不改变则需要深拷贝)
test();

二、深拷贝

要实现深拷贝有很多办法,有最简单的 JSON.parse() 方法,也有常用的递归拷贝方法,和ES5中的 Object.create() 方法。

- 方法一:使用 JSON.parse() 方法

 function deepClone(initalObj) {
      var obj = {};
      try {
          obj = JSON.parse(JSON.stringify(initalObj));
      }
      return obj;
  }
  var obj = {
      a: {
          a: "world",
          b: 21
      }
  }
  var cloneObj = deepClone(obj);
  cloneObj.a.a = "changed";
  console.log(obj.a.a); // "world"

缺点:

  • 该方法会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object
  • 这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。

- 方法二:递归拷贝

为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环。

// 深拷贝 递归1.1 写法一
function deepClone(obj) {
    var obj = {};
    for (var i in initalObj) {
        var prop = initalObj[i];
        // 避免相互引用对象导致死循环,
        if(prop === obj) {
            continue;
        }
        if (typeof prop === 'object') {
            obj[i] = (prop.constructor === Array) ? [] : {};
            arguments.callee(prop, obj[i]);
        } else {
            obj[i] = prop;
        }
    }
    return obj;
}
// 深拷贝 递归1.2 写法二
function deepCopy(p, c) {
  var c = c || {};
  for (var i in p) {
    if (typeof p[i] === 'object') {
      c[i] = (p[i].constructor === Array) ? [] : {};
      deepCopy(p[i], c[i]);
    } else {
       c[i] = p[i];
    }
  }
  return c;
}
// 深拷贝 递归1.3 写法二  比较全面的写法 参考至 (https://mp.weixin.qq.com/s/vXbFsG59L1Ba0DMcZeU2Bg)
function forEach(array, cloneTarget) {
  let index = -1;
  const length = array.length;
  while (++index < length) {
    cloneTarget(array[index], index);
  }
  return array;
}

function clone(target, map = new WeakMap()) {
  if (typeof target === 'object') {
    const isArray = Array.isArray(target);
    let cloneTarget = isArray ? [] : {};
    if (map.get(target)) {
      return target;
    }
    map.set(target, cloneTarget);
    const keys = isArray ? undefined : Object.keys(target);
    forEach(keys || target, (value, key) => {
      if (keys) key = value
      cloneTarget[key] = clone(target[key], map);
    });
    return cloneTarget;
  } else {
    return target;
  }
}
2.3 方法三:集合Object.assign()方法
// 深拷贝
function deepClone(initalObj) {
    var obj = {};
    for (var i in initalObj) {
        var prop = initalObj[i];
        // 避免相互引用对象导致死循环
        if(prop === obj) {
            continue;
        }
        if (typeof prop === 'object') {
            obj[i] = Object.assign(prop);
        } else {
            obj[i] = prop;
        }
    }
    return obj;
}

深拷贝1.3版本参考至: https://mp.weixin.qq.com/s/vXbFsG59L1Ba0DMcZeU2Bg (递归方法总结的很全面推荐)