跳转到内容

JavaScript深拷贝与浅拷贝

来自代码酷


JavaScript深拷贝与浅拷贝是处理对象复制时的两种核心方式,理解它们的区别对避免数据意外修改至关重要。本文将系统讲解其原理、实现方法及实际应用场景。

基本概念[编辑 | 编辑源代码]

浅拷贝(Shallow Copy)[编辑 | 编辑源代码]

浅拷贝仅复制对象的顶层属性。若属性是基本类型(如number, string),则直接复制值;若属性是引用类型(如object, array),则复制其内存地址(即共享同一引用)。

深拷贝(Deep Copy)[编辑 | 编辑源代码]

深拷贝会递归复制对象的所有层级,生成一个完全独立的副本,新旧对象互不影响。

graph LR A[原对象] -->|浅拷贝| B[新对象] A --> C[嵌套对象] B --> C D[原对象] -->|深拷贝| E[新对象] D --> F[嵌套对象] E --> G[新嵌套对象]

实现方式与示例[编辑 | 编辑源代码]

浅拷贝方法[编辑 | 编辑源代码]

1. 展开运算符(...)[编辑 | 编辑源代码]

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

shallowCopy.a = 3;      // 修改顶层属性
shallowCopy.b.c = 4;    // 修改嵌套属性

console.log(original);  // { a: 1, b: { c: 4 } } —— 嵌套对象被影响
console.log(shallowCopy); // { a: 3, b: { c: 4 } }

2. Object.assign()[编辑 | 编辑源代码]

const copy = Object.assign({}, original);

深拷贝方法[编辑 | 编辑源代码]

1. JSON.parse(JSON.stringify())[编辑 | 编辑源代码]

const deepCopy = JSON.parse(JSON.stringify(original));

deepCopy.b.c = 5;
console.log(original.b.c);  // 输出 2 —— 原对象不受影响
  • 限制:无法复制函数、Symbolundefined及循环引用。

2. 递归实现[编辑 | 编辑源代码]

function deepClone(obj, hash = new WeakMap()) {
    if (obj === null || typeof obj !== 'object') return obj;
    if (hash.has(obj)) return hash.get(obj);

    const result = Array.isArray(obj) ? [] : {};
    hash.set(obj, result);

    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            result[key] = deepClone(obj[key], hash);
        }
    }
    return result;
}

数学表达[编辑 | 编辑源代码]

对于对象O,其浅拷贝S和深拷贝D可表示为:

  • 浅拷贝:S={(k,v)v为基本类型则复制值,否则复制引用}
  • 深拷贝:D={(k,deepClone(v))kO}

实际应用场景[编辑 | 编辑源代码]

  • 浅拷贝适用场景:配置对象合并、快速创建相似对象(无需隔离嵌套数据)。
  • 深拷贝适用场景:状态管理(如Redux)、历史记录功能、避免数据污染。

性能对比[编辑 | 编辑源代码]

拷贝方式对比
方法 时间复杂度 适用场景
O(n)(n为顶层属性数) | 简单对象复制
O(nd)(d为嵌套深度) | 无特殊类型的对象
O(nd) | 复杂对象(含函数、循环引用)

常见误区[编辑 | 编辑源代码]

  • 误以为const copy = original是拷贝(实际是引用赋值)。
  • 忽视循环引用导致的栈溢出问题(递归深拷贝需用WeakMap处理)。

总结[编辑 | 编辑源代码]

理解深浅拷贝的差异能帮助开发者:

  1. 避免共享引用导致的数据意外修改
  2. 根据场景选择合适拷贝策略
  3. 优化程序性能与内存使用