js对象浅拷贝与深拷贝

2,407 阅读2分钟

js对象浅拷贝与深拷贝

  • javascript中Object、Array是引用类型,栈内存中存储内存地址,堆内存存储值,因此在引用类型的拷贝中,存在浅拷贝、深拷贝。下文将通过示例加以说明。
  • 因为基本类型和引用类型的区别,注意函数值传递的区别,这里不加以详细说明。

浅拷贝

  • 如果对象里只用值类型的属性,可以使用扩展符(...)或 Object.assign(...)拷贝
<!--扩展符-->
var obj = { foo: "foo", bar: "bar" };
var copy = { ...obj }; // Object { foo: "foo", bar: "bar" }

<!--assign-->
var obj = { foo: "foo", bar: "bar" };
var copy = Object.assign({}, obj); // Object { foo: "foo", bar: "bar" }
  • 注意,上述两种方法都可用于将属性值从多个源对象复制到目标对象,示例
var obj1 = { foo: "foo" };
var obj2 = { bar: "bar" };

var copySpread = { ...obj1, ...obj2 }; // Object { foo: "foo", bar: "bar" }
var copyAssign = Object.assign({}, obj1, obj2); // Object { foo: "foo", bar: "bar" }
  • 上述方法的问题在于,对于具有属性本身对象的对象,只复制引用,示例
var foo = { a: 0 , b: { c: 0 } };
var copy = { ...foo };

copy.a = 1;
copy.b.c = 2;

console.dir(foo); // { a: 0, b: { c: 2 } }
console.dir(copy); // { a: 1, b: { c: 2 } }
console.log(copy.b === foo.b) // true

深拷贝

  • 利用JSON将对象序列化为字符串,然后将其反序列化。
var obj = { a: 0, b: { c: 0 } };
var copy = JSON.parse(JSON.stringify(obj));
console.log(copy.a === obj.b) // false
  • 遗憾的是,此方法仅在源对象包含可序列化值类型且没有任何循环引用时才有效。
<!--函数丢失-->
var obj = { a: 0, b: { c: 0 }, d: function() {console.log('------>')} };
var copy = JSON.parse(JSON.stringify(obj));

console.log(copy) // {a: 0, b: {…}}

<!--日期对象将被转换为字符串化上以ISO格式打印-->
var obj = { a: 0, b: { c: 0 }, f: new Date() };
var copy = JSON.parse(JSON.stringify(obj));
console.log(copy) // {a: 0, b: {…}, f: "2018-12-07T08:11:48.210Z"}

  • NODE.JS中的深度复制
  • 从8.0.0版开始,Node.js提供了与结构化克隆兼容的序列化API。
  • 对于低于8.0.0的版本或更稳定的实现,可以使用lodash的cloneDeep方法,该方法也基于结构化克隆算法。
const v8 = require('v8');
const buf = v8.serialize({a: 'foo', b: new Date()});
const cloned = v8.deserialize(buf);
cloned.b.getMonth();

小结

  • 总而言之,在Javascript中复制对象的最佳算法在很大程度上取决于您要复制的对象的上下文和类型。虽然lodash是通用深层复制功能最安全的选择,我们也可以动手写一个简单的适用日期拷贝的深度拷贝。
  • 适用日期的深拷贝实现-示例
function deepClone(obj) {
  var copy;

  // Handle the 3 simple types, and null or undefined
  if (null == obj || "object" != typeof obj) return obj;

  // Handle Date
  if (obj instanceof Date) {
    copy = new Date();
    copy.setTime(obj.getTime());
    return copy;
  }

  // Handle Array
  if (obj instanceof Array) {
    copy = [];
    for (var i = 0, len = obj.length; i < len; i++) {
        copy[i] = deepClone(obj[i]);
    }
    return copy;
  }

  // Handle Function
  if (obj instanceof Function) {
    copy = function() {
      return obj.apply(this, arguments);
    }
    return copy;
  }

  // Handle Object
  if (obj instanceof Object) {
      copy = {};
      for (var attr in obj) {
          if (obj.hasOwnProperty(attr)) copy[attr] = deepClone(obj[attr]);
      }
      return copy;
  }

  throw new Error("Unable to copy obj as type isn't supported " + obj.constructor.name);
}

参考链接