深拷贝与浅拷贝:一篇彻底讲明白的入门博客

文章目录

前言

在学习 JavaScript、Python、Java 等编程语言时,"深拷贝"和"浅拷贝"几乎是绕不开的知识点。

很多初学者都会遇到这种情况:

js 复制代码
const a = { name: "Tom" };
const b = a;

b.name = "Jack";

console.log(a.name);

结果输出:

js 复制代码
Jack

很多人会疑惑:

"我改的是 b,为什么 a 也变了?"

其实,这背后就涉及到"拷贝"的本质。


一、什么是拷贝

所谓"拷贝",本质上就是:

把一个变量的数据复制给另一个变量。

但这里有一个关键问题:

到底复制的是"数据本身",还是"数据地址"?

这就引出了:

  • 浅拷贝(Shallow Copy)
  • 深拷贝(Deep Copy)

二、为什么会有深浅拷贝

先理解一个核心概念:

基本类型

例如:

js 复制代码
Number
String
Boolean
null
undefined

它们的数据直接存储在变量里。

例如:

js 复制代码
let a = 10;
let b = a;

b = 20;

这时候:

js 复制代码
a === 10
b === 20

因为复制的是值本身。


引用类型

例如:

js 复制代码
Object
Array
Function

它们真正存储的是:

内存地址

举个例子:

js 复制代码
const obj1 = {
    name: "Tom"
};

const obj2 = obj1;

实际上:

text 复制代码
obj1  ---> 内存地址A
obj2  ---> 内存地址A

两个变量指向的是同一块内存。

所以修改 obj2 时,obj1 也会变化。


三、什么是浅拷贝

浅拷贝:

只复制第一层数据。

如果对象里面还有对象,那么内部对象仍然共享同一个地址。

例如:

js 复制代码
const obj1 = {
    name: "Tom",
    info: {
        age: 18
    }
};

const obj2 = { ...obj1 };

obj2.name = "Jack";
obj2.info.age = 20;

console.log(obj1);

结果:

js 复制代码
{
  name: "Tom",
  info: {
    age: 20
  }
}

为什么 name 没变,而 age 变了?

因为:

js 复制代码
name

是第一层,已经复制了一份。

而:

js 复制代码
info

是对象。

浅拷贝只复制它的地址。

所以:

text 复制代码
obj1.info === obj2.info

结果为:

js 复制代码
true

四、常见的浅拷贝方式

1. Object.assign()

js 复制代码
const newObj = Object.assign({}, oldObj);

2. 展开运算符

这是现在最常用的方式:

js 复制代码
const newObj = { ...oldObj };

数组也一样:

js 复制代码
const arr2 = [...arr1];

3. slice()

数组浅拷贝:

js 复制代码
const arr2 = arr1.slice();

五、什么是深拷贝

深拷贝:

完全复制一个对象,包括对象里面的对象。

复制之后:

  • 两个对象完全独立
  • 修改新对象不会影响旧对象

例如:

js 复制代码
const obj1 = {
    name: "Tom",
    info: {
        age: 18
    }
};

const obj2 = structuredClone(obj1);

obj2.info.age = 20;

console.log(obj1.info.age);

结果:

js 复制代码
18

因为:

text 复制代码
obj1.info !== obj2.info

它们已经不是同一个对象了。


六、常见的深拷贝方式

1. structuredClone(推荐)

现代浏览器与 Node.js 已支持:

js 复制代码
const newObj = structuredClone(oldObj);

优点:

  • 真正深拷贝
  • 支持数组、对象
  • 使用简单

缺点:

  • 老版本环境可能不支持

2. JSON 方式

经典写法:

js 复制代码
const newObj = JSON.parse(JSON.stringify(oldObj));

这是很多初学者最先接触的方法。

优点:

  • 简单
  • 兼容性好

缺点非常明显:

无法处理:

js 复制代码
function
undefined
Symbol
Date
Map
Set
循环引用

例如:

js 复制代码
const obj = {
    time: new Date()
};

const copy = JSON.parse(JSON.stringify(obj));

console.log(copy.time);

结果:

js 复制代码
字符串

Date 类型丢失了。


3. 递归手写深拷贝

面试里非常经典。

核心思想:

text 复制代码
如果是基本类型,直接返回
如果是对象,则递归复制每个属性

简单实现:

js 复制代码
function deepClone(obj) {
    if (obj === null || typeof obj !== "object") {
        return obj;
    }

    const newObj = Array.isArray(obj) ? [] : {};

    for (let key in obj) {
        newObj[key] = deepClone(obj[key]);
    }

    return newObj;
}

这是理解深拷贝原理最好的方式。


七、深拷贝与浅拷贝的核心区别

对比项 浅拷贝 深拷贝
是否复制嵌套对象
是否共享引用地址 不会
修改新对象影响旧对象 可能影响 不影响
性能 较快 较慢
使用场景 简单数据 复杂嵌套数据

你可以简单理解:

text 复制代码
浅拷贝:复制"表面"
深拷贝:彻底复制

八、实际开发中该怎么选

很多人学完后最想知道的是:

"项目里到底该用哪个?"

其实很简单。

用浅拷贝的情况

如果数据结构不复杂:

js 复制代码
{
   name: "Tom",
   age: 18
}

直接:

js 复制代码
...
Object.assign()

就够用了。

性能更好。


用深拷贝的情况

如果存在:

  • 对象嵌套对象
  • 数组嵌套对象
  • React/Vue 状态管理
  • 后端接口数据加工

那么更推荐:

js 复制代码
structuredClone()

或者成熟库:

js 复制代码
lodash.cloneDeep()

否则很容易出现:

text 复制代码
一个地方修改数据
整个页面一起变化

这种 Bug 在真实项目里非常常见。


总结

深拷贝和浅拷贝,本质区别只有一句话:

text 复制代码
是否复制"引用地址"

浅拷贝:

text 复制代码
只复制第一层

深拷贝:

text 复制代码
递归复制所有层

初学阶段最重要的不是死记 API,而是理解:

text 复制代码
对象存的是地址

一旦理解这一点,深浅拷贝的问题就会瞬间清晰很多。

相关推荐
星恒随风2 分钟前
C++ STL 详解:set 与 multiset 的使用、区间查询和算法应用
开发语言·c++·笔记·学习·算法
数聚天成DeepSData10 分钟前
外贸海关进出口数据去哪免费下载?从统计到明细的查找指南
linux·服务器·开发语言·前端·网络·人工智能·自然语言处理
再卷也是菜12 分钟前
C++11支持并发库
开发语言·c++
Lhan.zzZ15 分钟前
QML 开发中的“陷阱”:动态模型更新时,ComboBox 索引为何总是失效?
开发语言·qt
半夜里咳嗽的狼16 分钟前
组件进侧栏就变形?用 CSS Container Queries 把响应式边界收回组件
前端·css
xx240622 分钟前
前端性能优化笔记
前端·笔记·性能优化
带娃的IT创业者28 分钟前
单文件架构的极致美学:深入解析 Bento 的 HTML 幻灯片技术实现
前端·架构·html·web开发·bento·单文件架构
小园子的小菜30 分钟前
Java 并发四大工具类深度解析:CountDownLatch、CyclicBarrier、Semaphore、Exchanger 原理、源码与面试考点
java·开发语言·面试
muddjsv31 分钟前
CSS 值的完整计算过程:指定值、计算值、使用值、实际值与动态依赖
前端·css
恋猫de小郭32 分钟前
KotlinLLM 开源 ,一个可以在运行时自己生成永久 Kotlin 代码的 Agent 库
android·前端·人工智能