JavaScript深拷贝浅拷贝

一、浅拷贝以及方法

引用类型,a,b保存内存中同一个地址空间,当a修改了变量,b也发生变化,因为修改的是内存对象,他们共同引用了同一个内存对象,发生改变。这种就是浅拷贝。

浅拷贝方法

  1. 引用变量赋值:直接把一个引用变量,赋值给另一个变量。完成浅拷贝

  2. 遍历并赋值给新对象:

const shallowCopy = (obj) => {
  // 判断参数是数组还是对象
  const result = Array.isArray(obj) ? [] : {};
  for(let key in obj) {
    // 使用hasOwnProperty来判断是否是自身属性
    // 只拷贝自身属性,不拷贝原型链上的属性
    if(obj.hasOwnProperty(key)){
      result[key] = obj[key];
    }
  }

  return result;
}

二、深拷贝以及方法

深度拷贝后,两个对象在内存中完全不同,互不影响。

首先要明白,JS里面,深度复制没有一个完美答案。 当一个数据可JSON,他的深度复制才有意义。

深拷贝方法

  1. JSON转化

JSON的方法对,属性值是undefined,正则,函数,以及产生循环引用的数据对象都不行。

// JSON的方法对,undefined,循环引用,正则,函数,都不行

const obj = {
  arr: [111, 222],
  obj: { key: '对象' },
  a: () => { console.log('函数') },
  date: new Date(),
  reg: /正则/ig,
  testValue: undefined,
  testValue: null,
}

const shadowCopyObj = JSON.parse(JSON.stringify(obj))

console.log(shadowCopyObj)
// output
// { arr: [ 111, 222 ],
//   obj: { key: '对象' },
//   date: '2020-04-20T08:03:01.178Z',
//   reg: {},
//   testValue: null }
  1. for…in 递归

无法解决 函数,正则,日期,循环引用

const obj = {
  arr: [111, 222],
  obj: { key: '对象' },
  a: () => { console.log('函数') },
  date: new Date(),
  reg: /正则/ig,
  testValue: undefined,
  testValue: null,
}

function isObj(obj) {
  return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj) {
  let tempObj = Array.isArray(obj) ? [] : {}
  for (let key in obj) {
    tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]
  }
  return tempObj
}

const deepCopyObj = deepCopy(obj)
console.log(deepCopy(obj))

// output"
// { arr: [ 111, 222 ],
//   obj: { key: '对象' },
//   a: {},
//   date: {},
//   reg: {},
//   testValue: null }
  1. 结构性复制,可惜是异步的

//https://developer.mozilla.org/zh-CN/docs/Web/Guide/API/DOM/The_structured_clone_algorithm#%E7%9B%B8%E5%85%B3%E9%93%BE%E6%8E%A5

没有完整的深拷贝方案。这是一个从上层不被支持的功能。

三、实用性的深拷贝实现

更新方法:

2023年03月17日,一个更可塑的方法。 尤其是对于深拷贝一个函数,关注他的处理方式。


function deepCopy(obj, hash = new WeakMap()) {
    if (obj === null) return obj;
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);
    if (typeof obj === 'function') {
      return new Function(`return ${obj.toString()};`)();
    }
    if (typeof obj !== 'object') return obj;
    if (hash.has(obj)) return hash.get(obj);

    // 使用obj构建原型
    let result = new Object.create(obj); // 或者 = new obj.constructor();
    hash.set(obj, result);

    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            // 再次传入 hash 很重要,不传递会爆栈
            result[key] = deepCopy(obj[key], hash);
        }
    }

    return result;
}

// 示例
let originalObj = {a: 1, b: {c: 2}, f: function(){ console.log('origin'); return 'origin'}};
originalObj.self = originalObj;

let copiedObj = deepCopy(originalObj);

console.log(copiedObj); // 输出:{a: 1, b: {c: 2}, self: [Circular]}
originalObj.b.c = 3;
console.log(copiedObj); // 输出:{a: 1, b: {c: 2}, self: [Circular]}

console.log(originalObj.f === copiedObj.f) // false 完成函数深拷贝

参考

Mark24

Everything can Mix.