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

文章目录

前言

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

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

相关推荐
大可-20 小时前
CSDN博客-星火知识库教程
前端·javascript·vue.js·elementui·html
fhqlongteng20 小时前
RK3576上electron调用GPU的功能设置方法
前端·javascript·electron·gpu·rk3576
weixin_3975740920 小时前
ReAct推理链的工程化实现与最佳实践
前端·react.js·前端框架
我命由我1234520 小时前
Dart - 数字类型、布尔类型、列表类型
android·开发语言·flutter·ios·uni-app·android jetpack·移动端
艾iYYY20 小时前
详解string类的基础用法
c语言·开发语言·c++·算法
吃好睡好便好20 小时前
创建对角矩阵
开发语言·学习·线性代数·算法·matlab·信息可视化·矩阵
冰暮流星20 小时前
javascript之window对象方法
开发语言·javascript·ecmascript
c++之路20 小时前
责任链模式(Chain of Responsibility Pattern)
java·前端·责任链模式
woniu_buhui_fei20 小时前
JDK8 开发最常用的新特性
java·开发语言
xyq202420 小时前
XML 服务器
开发语言