浅拷贝与深拷贝:本质区别

浅拷贝 (Shallow Copy)

本质:只复制一层,共享深层。

浅拷贝的本质是创建一个新的对象/数组,然后将原始对象/数组的“第一层”属性值复制到新对象/数组中

  • 如果第一层的属性值是值类型stringnumber等),那么就直接复制这个

  • 如果第一层的属性值是引用类型(另一个对象或数组),那么就只复制这个引用(内存地址)

这意味着,新对象和旧对象的第一层是独立的,但它们共享了所有深层次的对象。

一个生动的比喻:

想象你有一个工具箱(原始对象),里面装着螺丝刀(值类型)和一个装满钉子的盒子(引用类型)。

  • 浅拷贝就像是你买了一个新的空工具箱(新对象),然后:

    1. 把你原来的螺丝刀拿出来,买一把一模一样的新螺丝刀放进新工具箱。

    2. 把你原来的那个装钉子的盒子直接拿过来,也放进新工具箱。

  • 结果:你现在有两个工具箱。你用新工具箱里的螺丝刀,不会影响旧的。但是,两个工具箱里装的是同一个钉子盒。你从任何一个工具箱里拿出钉子用掉,另一个工具箱里的钉子也会变少。

浅拷贝的实现方法及示例

常见的浅拷贝方法有 Object.assign() 和扩展运算符 ...

const original = {
    name: "My Component", // 值类型
    version: 1.0,         // 值类型
    config: {             // 引用类型
        theme: "dark",
        permissions: ["read", "write"]
    }
};

// 使用扩展运算符进行浅拷贝
const shallowCopy = { ...original };

// 1. 修改顶层的值类型属性
shallowCopy.version = 1.1;

console.log(original.version); // 1.0 (原始对象不受影响,因为 version 是值类型)
console.log(shallowCopy.version); // 1.1

// 2. 修改嵌套的引用类型属性
shallowCopy.config.theme = "light";

console.log(original.config.theme); // "light" (⚠️ 原始对象被意外修改了!)
console.log(shallowCopy.config.theme); // "light"

// 验证:它们共享同一个 config 对象
console.log(original.config === shallowCopy.config); // true

深拷贝 (Deep Copy)

本质:彻底复制,完全独立。

深拷贝的本质是创建一个全新的、完全独立的对象/数组副本。它会递归地遍历原始对象的所有属性,直到最深层:

  • 每当遇到一个引用类型(对象或数组),它都会创建一个新的对象或数组,然后把内容复制过去,而不是只复制引用。

  • 对于值类型,它和浅拷贝一样,直接复制值。

继续上面的比喻:

  • 深拷贝就像是你不仅买了一个新的空工具箱,而且:

    1. 买了一把一模一样的新螺丝刀放进去。

    2. 还买了一个新的空钉子盒,然后把你旧钉子盒里的钉子一个一个地复制到这个新盒子里。

  • 结果:你现在有两个完全独立的工具箱。它们里面的所有东西(包括钉子盒)都是独立的。你对其中任何一个的任何操作,都不会影响另一个。

深拷贝的实现方法及示例

1. JSON.parse(JSON.stringify(object)) (简单但不完美)

这是最简单快捷的方法,但有几个致命缺陷:

  • 会忽略 undefinedSymbol 和函数。

  • 不能处理循环引用(对象内部属性互相引用),会报错。

  • 会把 Date 对象转换成字符串。

const original = {
    name: "My App",
    createdAt: new Date(),
    update: function() { console.log('update'); },
    config: {
        id: Symbol('id')
    }
};

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

console.log(jsonCopy);
/* 输出:
{
  "name": "My App",
  "createdAt": "2023-10-27T...", // Date 变成了字符串
  "config": {} // update 函数和 symbol 属性都丢失了
}
*/

2. structuredClone() (现代浏览器的标准答案)

这是一个新的 Web API,专门用来进行深拷贝。它功能强大,能处理循环引用和更多的数据类型(如 DateRegExpMapSet 等)。是目前首选的内置方法

const original = {
    name: "My Component",
    config: {
        theme: "dark"
    }
};
original.self = original; // 创建一个循环引用

const deepCopy = structuredClone(original);

// 修改嵌套属性
deepCopy.config.theme = "light";

console.log(original.config.theme); // "dark" (原始对象完全不受影响)
console.log(deepCopy.config.theme); // "light"

// 验证:它们不共享任何内部对象
console.log(original.config === deepCopy.config); // false
console.log(original === deepCopy); // false

注意:structuredClone 也不能拷贝函数和 Symbol 属性。

3. 使用库(如 Lodash 的 _.cloneDeep()

在 structuredClone 出现之前以及在需要兼容旧环境或拷贝函数等复杂场景时,库函数是最可靠的选择。


总结:本质区别的核心

特性浅拷贝 (Shallow Copy)深拷贝 (Deep Copy)
核心本质创建新对象,但共享内部的引用类型数据创建新对象,并递归地复制所有内部数据,完全独立
复制层级只复制第一层递归复制所有层级
内存关系新旧对象中的引用类型属性指向同一块堆内存新旧对象完全独立,指向不同的堆内存
适用场景当对象只有一层,或你明确知道不需要修改嵌套对象时当你需要一个对象的完整、独立的快照,以避免修改原始数据时
性能快,开销小慢,开销大,因为需要遍历和创建更多对象

面试时,能够清晰地解释出“浅拷贝只复制引用,深拷贝递归创建新对象”,并用“工具箱和钉子盒”这样的比喻来辅助说明,就能充分展示你对这个概念的深刻理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值