浅拷贝与深拷贝

187 阅读2分钟

啥是浅拷贝与深拷贝?

浅拷贝:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间

深拷贝:拷贝出来的目标对象的指针和源对象的指针指向的内存空间不是同一块空间

看不懂?举栗子

let arr = [1,2,3];
let arrCopy = arr;
arrCopy[0] = 0;
console.log(arr) // [0,2,3]

这个就是浅拷贝,就算你新建一个变量引用的还是原来的数组,那深拷贝就是新建的变量不跟原来引用一样就可以了?

let arr = [1,2,3];
let arrCopy = [...arr];
arrCopy[0] = 0;
console.log(arr) // [1,2,3]

不对这个还不算是深拷贝,因为还不够深,如果是二维数组,里面的引用关系还是引原来的

let arr = [1,2,[3,4]];
let arrCopy = [...arr];
arrCopy[2][0] = 0;
console.log(arr) // [1,2,[0,4]]

那咋深拷贝?

采用 JSON.stringify() 与 JSON.parse() 结合

let arr = [1,2,[3,4]];
let arrCopy = JSON.parse(JSON.stringify(arr));
arrCopy[2][0] = 0;
console.log(arr) // [1,2,[3,4]]

但使用这个会有坑请注意:

  1. 如果被复制的含有时间对象,复制后的时间对象只是字符串的形式。
let arr = [new Date()];
let arrCopy = JSON.parse(JSON.stringify(arr));
console.log(arr[0])  // Sat Jul 27 2019 00:00:00 GMT+0800 (中国标准时间)
console.log(arrCopy[0]) // "2019-07-27T00:00:00.099Z"
  1. 如果被复制的含有RegExp、Error对象,复制后只能得到空对象。
let arr = [new RegExp()];
let arrCopy = JSON.parse(JSON.stringify(arr));
console.log(arr[0])  // /(?:)/
console.log(arrCopy[0]) // Object {}
  1. 如果被复制的含有undefined或NaN、Infinity与-Infinity,复制后只能得到null。
let arr = [undefined,NaN,Infinity,-Infinity];
let arrCopy = JSON.parse(JSON.stringify(arr));
console.log(arr)  // [undefined,NaN,Infinity,-Infinity]
console.log(arrCopy) // [null,null,null,null]
  1. 如果被复制的含有函数又或者是继承来的函数,复制后这些函数都不存在了。
let obj = {
  say: function() {
     console.log('hello world')
   }
}
let objCopy = JSON.parse(JSON.stringify(obj));
console.log(obj.say())  // hello world
console.log(objCopy.say()) // Uncaught TypeError: objCopy.say is not a function

虽然简便,但如果不能忍受那么多坑还是乖乖使用其他办法吧

使用JQuery里面的$.extend深拷贝

let arr = [1,2,[3,4]];
let arrCopy = [];
$.extend(true,arrCopy,arr);
arrCopy[2][0] = 0;
console.log(arrCopy) // [1,2,[0,4]];
console.log(arr) // [1,2,[3,4]];

采用递归方式

function deepClone(data) {
  const type = this.judgeType(data);
  let obj;
  if (type === 'array') {
    obj = [];
    for (let i = 0, len = data.length; i < len; i++) {
      obj.push(this.deepClone(data[i]));
    }
  } else if (type === 'object') {
    obj = {};
    for (const key in data) {
      obj[key] = this.deepClone(data[key]);
    }
  } else {
    return data;  
  }
  return obj;
}

function judgeType(obj) {
  const map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object',
  };
  if (obj instanceof Element) {
    return 'element';
  }
  return map[Object.prototype.toString.call(obj)];
}

let arr = [1,2,[3,4]];
let arrCopy = deepClone(arr);
arrCopy[2][0] = 0;
console.log(arrCopy) // [1,2,[0,4]];
console.log(arr) // [1,2,[3,4]];