如何实现深拷贝和浅拷贝?

在JavaScript中处理对象和数组时,理解拷贝行为是避免意外副作用的关键。拷贝分为浅拷贝与深拷贝,其核心区别在于对嵌套对象引用地址的处理方式。

什么是浅拷贝

浅拷贝创建一个新对象,并将原对象顶层属性的值复制到新对象。如果属性值是基本类型,则直接复制值;如果是引用类型(如对象、数组),则复制其内存地址。这意味着新旧对象会共享嵌套的对象。

const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };

original.a = 10;
original.b.c = 20;

console.log(shallowCopy.a); // 1 (未受影响)
console.log(shallowCopy.b.c); // 20 (共享对象被修改)

常见的浅拷贝方法包括使用扩展运算符...Object.assign({}, obj)以及数组的slice()concat()方法。

什么是深拷贝

深拷贝会创建一个全新的对象,并递归地复制原对象中的所有层级,确保新旧对象完全独立,不共享任何引用类型的属性。

const original = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(original));

original.b.c = 20;
console.log(deepCopy.b.c); // 2 (完全独立,未受影响)

使用JSON.stringifyJSON.parse是实现简单深拷贝的快捷方式。但这种方法存在局限性:它会忽略函数、undefinedSymbol和循环引用,同时会将Date对象转为字符串。

实现一个健壮的深拷贝函数

为了处理更复杂的情况,需要实现一个递归拷贝函数。基础版本如下,但需要注意处理循环引用等边界情况。

function deepClone(source) {
  if (source === null || typeof source !== 'object') {
    return source;
  }
  const target = Array.isArray(source) ? [] : {};
  for (let key in source) {
    if (source.hasOwnProperty(key)) {
      target[key] = deepClone(source[key]);
    }
  }
  return target;
}

这个函数递归地复制所有自身可枚举属性。在实际生产环境中,还需要考虑拷贝原型链、处理DateRegExp等内置对象,以及使用WeakMap解决循环引用问题,防止栈溢出。

在项目中选择拷贝方式时,需要权衡需求与性能。如果数据没有嵌套引用或你确实需要共享嵌套对象,使用浅拷贝。如果需要完全独立的数据副本,且数据是简单的JSON结构,JSON方法很方便。对于包含复杂类型或需要高性能的场景,建议使用成熟的工具库如Lodash中的_.cloneDeep方法,它们已经妥善处理了各种边界情况。

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容