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

文章目录

前言

在学习 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 复制代码
对象存的是地址

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

相关推荐
阿成学长_Cain16 小时前
Linux telinit 命令详解:运行级别切换|关机重启|系统维护一站式掌握
linux·运维·前端·网络
Patrick_Wilson16 小时前
最佳实践是有保质期的:从一次 CDN external 白屏事故说起
前端·性能优化·前端工程化
言乐616 小时前
Python实现建造微服务商城后台
开发语言·python·算法·微服务·架构
YHHLAI16 小时前
[特殊字符] Agent 智能体开发实战 · 第一课:Tool Use —— 让大模型自动干活
前端·人工智能
nuIl16 小时前
我把 5 个编码 Agent 塞进了一个 npm 包
前端·agent·claude
L-影16 小时前
FastAPI 静态文件:Web 页面的“固定展柜”与“加速引擎”
前端·fastapi
陪我去看海16 小时前
受够了 AI 写完代码留下一堆端口?我做了一个端口管理App!
前端·macos·vibecoding
Xuepoo16 小时前
我抛弃了 DOM,但保留了无障碍访问
前端
李明卫杭州16 小时前
Vue2 vs Vue3 的 h 函数:渲染函数完整迁移指南
前端
cui_ruicheng17 小时前
Python从入门到实战(六):非序列容器
开发语言·python