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

文章目录

前言

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

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

相关推荐
橙子家4 小时前
浏览器缓存之【基础键值存储】:Local storage 和 Session storage
前端
星星在线7 小时前
MusicFree:一个「All in One」的个人音乐服务器,让听歌回归简单
前端·后端
IT_陈寒8 小时前
Redis的SETNX并发问题让我加了三天班
前端·人工智能·后端
demo007x8 小时前
Docling 文档转换以及技术架构分析
前端·后端·程序员
京东云开发者9 小时前
京东市民服务又“上新”!这次是黑龙江“龙易办”
前端
袋鱼不重9 小时前
我的神奇同事,AI 用多了居然写了个 Open In Codex
前端·后端·ai编程
竹林8189 小时前
Web3表单签名验证:我用 wagmi 和 ethers 给 DApp 加了一个“免密登录”,踩坑记录全在这了
javascript
用户6990304848759 小时前
try catch使用场景 处理同步代码错误兼容用的
javascript·uni-app
LDR0069 小时前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术9 小时前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript