对象深拷贝

95 阅读2分钟

一、浅拷贝

浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。
如果属性是基本类型,拷贝的就是基本类型的值,
如果属性是引用类型,拷贝的就是内存地址 , 所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

实现浅拷贝的方法

  1. Object.assign()
let obj1 = { person: {name: "kobe", age: 41},sports:'basketball' };
let obj2 = Object.assign({}, obj1);
obj2.person.name = "wade";
obj2.sports = 'football'
console.log(obj1); // { person: { name: 'wade', age: 41 }, sports: 'basketball' }
  1. 展开运算符...
let obj1 = { person: {name: "kobe", age: 41},sports:'basketball' };
let obj2 = {...obj1}
obj2.person.name = "wade";
obj2.sports = 'football'
console.log(obj1); // { person: { name: 'wade', age: 41 }, sports: 'basketball' }
  1. Array.prototype.concat()
let arr = [1, 3, {
  username: 'kobe'
  }];
let arr2 = arr.concat();    
arr2[2].username = 'wade';
console.log(arr); //[ 1, 3, { username: 'wade' } ]
  1. Array.prototype.slice()
let arr = [1, 3, {
  username: 'kobe'
  }];
let arr2 = arr.slice();    
arr2[2].username = 'wade';
console.log(arr); //[ 1, 3, { username: 'wade' } ]
  1. lodash库
var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.clone(obj1);
console.log(obj1.b.f === obj2.b.f);// true

二、深拷贝

深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

实现深拷贝的方法:

  1. JSON.parse(JSON.stringify(obj))
let arr = [1, 3, {
  username: ' kobe'
}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan'; 
console.log(arr, arr4)
// arr和arr4是两个独立的对象
// [1,3,{"username": " kobe"}]
// [1,3,{"username": "duncan"}]

注意事项:

  1. 拷贝的对象的值中如果有函数,undefined,symbol则经过JSON.stringify()序列化后的JSON字符串中这个键值对会消失
  2. 无法拷贝不可枚举的属性,无法拷贝对象的原型链
  3. 拷贝Date引用类型会变成字符串
  4. 拷贝RegExp引用类型会变成空对象
  5. 对象中含有NaN、Infinity和-Infinity,则序列化的结果会变成null
  6. 无法拷贝对象的循环应用(即obj[key] = obj)

虽说通过JSON.stringify()方法深拷贝对象也有很多无法实现的功能,但是对于日常的开发需求(对象和数组),使用这种方法是最简单和快捷的

var obj = {
  level1: {
    level2: {
      level3: {
        a: function() {},
        b: undefined,
        c: Symbol('aa'),
        d: new Date(),
        e: new RegExp(/test/),
        f: NaN,
        g: Infinity,
        h: -Infinity,
        i: obj
      }
    }
  }
}
var copy = JSON.parse(JSON.stringify(obj))
// copy的值如下:
// {
//   "level1": {
//       "level2": {
//           "level3": {
//               "d": "2022-02-28T13:32:16.003Z",
//               "e": {},
//               "f": null,
//               "g": null,
//               "h": null
//           }
//       }
//   }
// }
  1. 函数库lodash的_.cloneDeep方法
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
  1. 递归实现深拷贝
function deepClone(obj, map = new Map()) {
  if (obj === null) return obj;
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  // 普通值或函数不需要深拷贝
  if (typeof obj !== "object") return obj;

  let cloneObj = Array.isArray(obj) ? [] : {};
  // 防止循环引用
  if (map.get(obj)) {
    return map.get(obj)
  }
  map.set(obj, cloneObj)
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key], map)
    }
  }
  return cloneObj;
}

let obj = { name: 1, address: { x: 100 } };
obj.address.data = obj;
let copyObj = deepClone(obj);
obj.address.x = 200;
console.log(copyObj)