商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > 一次彻底搞懂JavaScript中的引用赋值、浅拷贝和深拷贝

一次彻底搞懂JavaScript中的引用赋值、浅拷贝和深拷贝

  发布于2026-07-23 阅读(0)

扫一扫,手机访问

前言

不少人在日常开发中,一碰到深浅拷贝就犯迷糊——引用赋值、浅拷贝、深拷贝,这三个概念到底差在哪?更头疼的是,稍不留神就把原始数据改了,或者拷贝不彻底,冒出些莫名其妙的bug。别急,今天咱们就用最直白的话、配上清晰的图示和大量能跑起来的代码示例,把这事儿彻底掰扯清楚。不过在动手之前,得先聊聊Ja vaScript的数据类型和内存到底是什么样的。

一、基础概念铺垫

1. Ja vaScript 数据类型分类

  • 基本数据类型:String、Number、Boolean、Null、Undefined、Symbol、BigInt。
  • 引用数据类型:Object、Array、Function,以及RegExp、Date、Map、Set、Promise、Error等内置对象。

2. 内存存储机制核心原理

JS引擎把内存分成了两块:栈内存(Stack)堆内存(Heap)。不同类型的数据会住进不同的“房间”,嵌套引用也遵守同样的规则。

特性栈内存 (Stack)堆内存 (Heap)
存储内容基本数据类型值、引用类型的指针引用数据类型的实际内容
数据结构后进先出 (LIFO)动态分配的树/图结构
分配方式连续内存,自动分配随机内存,动态分配
访问速度极快(直接CPU访问)较慢(通过指针间接访问)
大小限制小(通常1-8MB)大(可达GB级)
生命周期函数/块作用域结束自动释放由垃圾回收器(GC)管理

一次彻底搞懂Ja vaScript中的引用赋值、浅拷贝和深拷贝

上图展示了两类数据的内存访问流程:

  • 基本数据类型:变量直接从栈内存取数据,干脆利落。
  • 引用数据类型:变量先读栈内存里的指针(地址),再通过指针找到堆内存里的实际内容。说白了,就是绕了个弯。

二、不同类型的拷贝行为

1. 基本数据类型的拷贝

基本类型的拷贝没什么弯弯绕绕——值存在栈里,拷贝就是直接复制一份值,新变量和旧变量谁也不挨着谁。

let a = 10;
let b = a; // 直接复制值
b = 20;
console.log(a); // 10(不受 b 修改影响)
console.log(b); // 20

2. 引用数据类型的拷贝

方式一:引用赋值(非拷贝)

引用类型赋值时,默认干的事是引用赋值——复制的是指针地址,而不是对象本身。两个变量指向堆里的同一个对象,改一个,另一个也跟着变,避都避不开。

let a = [1, 2, 5];
let b = a; // 引用赋值(复制指针)
a[1] = 4; // 修改 a 指向的数组
console.log(a); // [1, 4, 5]
console.log(b); // [1, 4, 5](b 也受影响)
console.log(a === b); // true(指向同一个对象)

一次彻底搞懂Ja vaScript中的引用赋值、浅拷贝和深拷贝

方式二:浅拷贝

浅拷贝会创建一个新对象,但只复制第一层属性。规则很简单:

  • 碰到基本类型属性:直接复制值,新对象独立。
  • 碰到引用类型属性:只复制指针,不复制指针指向的对象。所以嵌套层还是共享的。

示例1:对数组 a = [1, 2, [3, 4], 5] 做浅拷贝得到 b

  • b[0]b[1]b[3] 是基本类型,改它们不影响 a
  • b[2] 是引用类型(数组),改 b[2] 会同时影响 a[2],因为指向同一个子数组。
  • 一次彻底搞懂Ja vaScript中的引用赋值、浅拷贝和深拷贝
const a = [1, 2, [3, 4], 5];
const b = [...a]; // 浅拷贝
a[0] = 100;
console.log(b[0]); // 1(不受影响,基本类型独立)
a[2][1] = 400; // 修改子数组元素
console.log(b[2][1]); // 400(受影响,共享子数组引用)

示例2

// 原始对象
const a = {
  name: "alice",  // 基本类型(栈内存存储值)
  profile: {      // 引用类型(堆内存存储对象,栈内存存储指针)
    age: 25,
    city: "beijing"
  }
};
// 使用扩展运算符进行浅拷贝
const b = { ...a };
// 修改浅拷贝对象
b.name = "bob";
b.profile.age = 30;
b.profile.city = "shanghai";
// 查看结果
console.log("原始对象 a.name:", a.name); // 输出:"alice"(基本类型值独立,不受影响)
console.log("原始对象 a.profile.age:", a.profile.age); // 输出:30(引用类型共享堆内存,被修改)
console.log("原始对象 a.profile.city:", a.profile.city); // 输出:"shanghai"(引用类型共享堆内存,被修改)
console.log("a.profile === b.profile:", a.profile === b.profile); // 输出:true(两者指向堆中同一个对象)

常见的浅拷贝方法

  1. 扩展运算符(推荐)
const obj = { a: 1, b: { c: 2 } };
const shallowCopy = { ...obj };
  1. Object.assign()
const obj = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, obj);
  1. 数组的 slice()、concat() 方法
const arr = [1, 2, { a: 3 }];
const shallowCopy1 = arr.slice();
const shallowCopy2 = [].concat(arr);
  1. Array.from()
const arr = [1, 2, { a: 3 }];
const shallowCopy = Array.from(arr);

方式三:深拷贝

深拷贝会创建一个全新的对象,递归复制所有层级属性——包括嵌套的引用类型。新旧对象完全独立,互不干扰。

实现方式 1:JSON 方法(最常用,但也有局限)

// 原始对象
const a = {
  name: "alice",  // 基本类型(栈内存存储值)
  profile: {      // 引用类型(堆内存存储对象)
    age: 25,
    city: "beijing"
  }
};
// 实现深拷贝
const deepCopy = JSON.parse(JSON.stringify(a));
// 修改深拷贝对象的属性
deepCopy.name = "bob"; // 修改基本类型
deepCopy.profile.age = 30; // 修改嵌套引用类型
deepCopy.profile.city = "shanghai";
// 查看结果对比
console.log("原始对象 a.name:", a.name); // 输出:"alice"(基本类型不受影响)
console.log("原始对象 a.profile.age:", a.profile.age); // 输出:25(嵌套引用类型也不受影响)
console.log("原始对象 a.profile.city:", a.profile.city); // 输出:"beijing"(嵌套引用类型完全独立)
console.log("a.profile === deepCopy.profile:", a.profile === deepCopy.profile); // 输出:false(两者指向堆中不同对象)

不过要注意,JSON.stringify 有硬伤:

  • 无法复制函数、undefined、Symbol
  • 日期对象会被转成字符串
  • 处理不了循环引用
  • 会丢失原型链信息

实现方式 2:递归实现(自定义深拷贝函数)

递归实现代码量稍大,这里不展开全貌,核心思路是遍历对象的所有属性,如果遇到引用类型就递归调用拷贝函数,一直复制到最底层。

三、深浅拷贝对比总结

类型引用类型内存地址第一层修改第二层修改
引用赋值引用复制相同相互影响相互影响
浅拷贝仅第一层值复制,嵌套层引用复制不同独立相互影响
深拷贝完全复制不同独立独立

说到底,深浅拷贝的核心区别就在对嵌套引用类型的处理方式上,这直接决定了拷贝后的对象到底有多独立:

  • 引用赋值:本质上不是拷贝,只是复制了引用指针。两个变量共享同一块堆内存,改哪一层都会互相影响,内存地址相同。
  • 浅拷贝:创建新的内存地址,但只复制第一层属性。基本类型属性修改后互不影响,可嵌套的引用类型属性还是共享的,改第二层就会暴露。
  • 深拷贝:完全创建一个新对象,递归复制所有层级。新旧对象拥有完全独立的内存空间,任何层级的修改都不会相互影响,内存地址不同。
本文转载于:https://www.jb51.net/javascript/355690s53.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注