当前位置: 首页 > news >正文

讲解 JavaScript 中的深拷贝和浅拷贝

一、核心概念:数据类型与内存存储

要理解深浅拷贝,首先要明白 JavaScript 的数据类型是如何存储的。

  1. 基本数据类型 (Primitive Types)

    • 包括:StringNumberBooleannullundefinedSymbolBigInt

    • 存储方式:值本身直接存储在栈内存中。

    • 赋值操作:将一个变量赋值给另一个变量时,会创建一个全新的副本。两个变量互不影响。

    let a = 10;
    let b = a; // 在栈内存中创建一个新的值 10,赋值给 bb = 20;
    console.log(a); // 10 (a 的值没有改变)
    console.log(b); // 20

    对于基本类型,赋值即等于深拷贝,不存在浅拷贝问题。

  2. 引用数据类型 (Reference Types)

    • 包括:ObjectArrayFunctionDate 等。

    • 存储方式:变量在栈内存中存储的是一个地址(指针),这个地址指向堆内存中存储的真正的对象数据。

    • 赋值操作:将一个对象赋值给另一个变量时,复制的是地址(指针),而不是堆内存中的数据本身。

    let obj1 = { name: 'Alice', age: 25 };
    let obj2 = obj1; // 只复制了地址,现在 obj1 和 obj2 指向堆内存中的同一个对象obj2.name = 'Bob'; // 通过 obj2 的地址修改了堆内存中的数据console.log(obj1.name); // 'Bob' (obj1 也变了!)
    console.log(obj2.name); // 'Bob'

    这种只复制地址,导致多个变量共享同一份数据的拷贝,就是浅拷贝


二、什么是浅拷贝 (Shallow Copy)?

定义:创建一个新对象,这个新对象有着原始对象属性值的一份精确拷贝

  • 如果属性是基本类型,拷贝的就是基本类型的

  • 如果属性是引用类型,拷贝的就是内存地址(指针)。

结果:新对象和原对象中的引用类型属性会指向同一个堆内存地址。修改其中一个,另一个也会随之改变。

实现浅拷贝的常用方法:

  1. 展开运算符 (...)

    const original = { a: 1, b: { c: 2 } };
    const shallowCopy = { ...original };original.a = 999; // 修改基本类型属性
    console.log(shallowCopy.a); // 1 (未受影响,因为是值的拷贝)original.b.c = 888; // 修改引用类型内部的属性
    console.log(shallowCopy.b.c); // 888 (也变了!因为它们共享同一个 { c: 2 } 对象)
  2. Object.assign()

    const original = { a: 1, b: { c: 2 } };
    const shallowCopy = Object.assign({}, original);
    // 同样的效果,修改 original.b.c 会导致 shallowCopy.b.c 也变化
  3. Array.prototype.slice() 和 Array.prototype.concat() (针对数组)

    const originalArray = [1, 2, { name: 'Alice' }];
    const shallowCopyArray = originalArray.slice();originalArray[2].name = 'Bob';
    console.log(shallowCopyArray[2].name); // 'Bob' (也变了)

三、什么是深拷贝 (Deep Copy)?

定义:创建一个新对象,并递归地拷贝原始对象中的所有属性及其嵌套的子对象。

  • 无论属性是基本类型还是引用类型,都会在堆内存中创建一个全新的副本

结果:新对象和原对象完全分离,互不干扰。修改任何一个对象,都不会影响另一个。

实现深拷贝的方法:

  1. JSON.parse(JSON.stringify()) (最常用但有缺陷)

    const original = { a: 1, b: { c: 2 } };
    const deepCopy = JSON.parse(JSON.stringify(original));original.b.c = 888;
    console.log(deepCopy.b.c); // 2 (未受影响,完全独立)

    ⚠️ 缺点

    • 无法拷贝函数undefinedSymbol

    • 无法处理循环引用(对象属性间接或直接引用自身),会报错。

    • 会丢弃对象的 constructor,所有对象都会变成 Object

    • 特殊对象如 Date 会变成字符串,RegExpError 对象等会变成空对象 {}

  2. 手动递归实现

    function deepClone(source) {// 如果不是对象或者是null,直接返回(基础类型和null)if (typeof source !== 'object' || source === null) {return source;}// 处理数组和对象const target = Array.isArray(source) ? [] : {};for (let key in source) {if (source.hasOwnProperty(key)) {// 递归拷贝每一个属性target[key] = deepClone(source[key]);}}return target;
    }const obj = { a: 1, b: { c: 2 } };
    const clonedObj = deepClone(obj);

    这种方法更可靠,但需要自己处理边界情况(如循环引用)。

  3. 使用第三方库
    这是生产环境中最可靠、最省事的方法。著名库如 Lodash 提供了经过充分测试的 _.cloneDeep() 方法。

    import _ from 'lodash';
    const original = { a: 1, b: { c: 2 } };
    const deepCopy = _.cloneDeep(original);

四、总结与对比

特性浅拷贝 (Shallow Copy)深拷贝 (Deep Copy)
核心区别只复制一层, deeper 引用类型属性共享同一内存递归复制所有层级,所有属性都独立
拷贝内容基本类型,引用类型地址所有层级的
修改效果修改原对象引用类型属性会影响拷贝对象修改原对象任何属性都不会影响拷贝对象
实现方法...Object.assign()array.slice()_.cloneDeep()JSON方法, 手动递归
性能,占用内存少,占用内存多(需要递归创建所有对象)

如何选择?

  • 需要浅拷贝时:当你确信对象没有嵌套引用类型,或者你希望嵌套对象是共享的(例如,共享一个配置对象)。

  • 需要深拷贝时:当你想创建一个完全独立、与原始对象毫无关联的副本,防止意外的副作用。这在状态管理(如 Redux)、函数式编程中非常常见。

理解深浅拷贝的关键在于彻底弄懂 JavaScript 的堆栈存储机制引用类型的概念。希望这个讲解对你有帮助!

http://www.dtcms.com/a/350364.html

相关文章:

  • PyPI 是什么?
  • CCleaner中文版:强大的系统优化与隐私保护工具,支持清理磁盘、注册表和卸载软件
  • `mysql_query()` 数据库查询函数
  • Ubuntu 22.04 中安装 ROS2 Humble
  • Java AI插件“飞算“实战测试:一键生成医院药品管理系统
  • Maven下载历史版本
  • 大模型微调 Prompt Tuning与P-Tuning 的区别?
  • 【44页PPT】DeepSeek在银行业务场景的应用(附下载方式)
  • AI 应用开发:从 Prompt 工程到实战应用开发
  • 基于RD算法的多目标SAR成像原理及MATLAB实现
  • 离线开发平台-HTTP数据同步到Doris数仓能力演示
  • GNN:用MPNN(消息传递神经网络)落地最短路径问题模型训练全流程
  • VS2010 在查找预编译头使用时跳过
  • 微服务商城构筑其一
  • [系统架构设计师]知识产权(二十)
  • 深度学习篇---混淆矩阵
  • 工业物联网如何提高生产效率
  • IsaacLab的关键函数位置
  • crc16是什么算法
  • LeetCode算法日记 - Day 21: 消失的两个数字、替换所有的问号
  • 解决 Ubuntu 环境下 ffmpeg 安装依赖问题(FileNotFoundError: ffprobe 错误处理)
  • 信号处理的核心机制:从保存、处理到可重入性与volatile
  • 做市商在期权市场中的盈利模式是什么?
  • 挖币与区块链技术有怎样的联系?
  • 【大白话解析】 OpenZeppelin 的 ECDSA 库:以太坊签名验证安全工具箱(附源代码)
  • ElasticSearch数据库(ES数据库)是什么???
  • docker安装ros
  • 网络编程1-基本概念、函数接口
  • 页面中嵌入Coze的Chat SDK
  • Hazelcast