【前端】JavaScript 变量引用、内存与数组赋值:深入解析三种情景

文章目录
- 💯前言
- 💯场景一:直接赋值与重新引用
- 为什么结果不是 `[3, 4, 5]`?
- 1. 引用与赋值的基本概念
- 2. 图示分析
- 关键总结
- 💯场景二:引用指向的变化
- 为什么结果还是 `[1, 2, 3]`?
- 对象引用与原始数据的区别
- 重新赋值与直接修改的差异
- 💯场景三:修改数组内容
- 为什么结果变成了 `[1, 2, 6]`?
- JavaScript 中的内存共享
- 💯深入理解 JavaScript 的内存模型与赋值行为
- 1. JavaScript 中的值类型和引用类型
- 2. 内存分配与垃圾回收
- 💯如何避免引用带来的问题
- 1. 浅拷贝与深拷贝
- 2. 使用 `Object.assign` 或 `lodash.cloneDeep`
- 💯小结

💯前言
- JavaScript 是一种基于对象的脚本语言,常用于前端开发。初学者在使用 JavaScript 时,通常会遇到一些关于变量引用和赋值的困惑。本文将详细讨论三种不同的代码场景,结合 JavaScript 的变量引用与内存模型,深入分析为什么这些代码输出会如此不同。希望通过对这些原理的探讨,能够帮助您更好地理解 JavaScript 中的变量引用机制。
💯场景一:直接赋值与重新引用
首先,我们来看以下的代码片段:
var arr = [1, 2, 3];
var newArr = arr;
newArr = [3, 4, 5];
console.log(arr); // 输出是什么?
运行结果:
[1, 2, 3]

为什么结果不是 [3, 4, 5]?
![]()
要理解这个问题,我们需要深入理解 JavaScript 中的变量赋值与引用的区别。
1. 引用与赋值的基本概念
![]()
在 JavaScript 中,基本数据类型(如 number、string 等)是按值传递的,而复杂数据类型(如数组、对象)是按引用传递的。
在代码中,var arr = [1, 2, 3] 创建了一个数组 [1, 2, 3],并且将其引用赋值给变量 arr。此时,arr 保存的是数组在内存中的引用(地址),而不是数组的值本身。
然后,执行 var newArr = arr;,这意味着 newArr 保存了与 arr 相同的引用。也就是说,newArr 和 arr 都指向了相同的内存地址,这个内存中存储的是数组 [1, 2, 3]。
当我们执行 newArr = [3, 4, 5]; 时,newArr 被重新赋值,指向了一个新的数组 [3, 4, 5] 的内存地址。此时,newArr 不再指向原来的数组 [1, 2, 3],而是指向了一个全新的数组。而 arr 依然保持指向原始数组的引用,因此打印 arr 时结果仍然是 [1, 2, 3]。
2. 图示分析
![]()
-
初始状态:
arr和newArr都指向[1, 2, 3]。
-
重新赋值后:
newArr指向新的数组[3, 4, 5]。arr依然指向原数组[1, 2, 3]。

关键总结
![]()
在 JavaScript 中,给一个变量赋予一个新的数组时,并不会改变原来的数组,而是创建了一个新的引用。如果希望改变所有引用同一数组的变量,那么需要对数组本身进行修改,而不是重新赋值。
💯场景二:引用指向的变化
接下来看第二个代码片段:
var arr = [1, 2, 3];
var newArr = arr;
arr = [4, 5, 6];
console.log(newArr); // 输出是什么?
运行结果:
[1, 2, 3]

为什么结果还是 [1, 2, 3]?
![]()
在这个场景中,我们遇到了类似的问题。在执行 var newArr = arr; 之后,newArr 和 arr 都指向同一个数组 [1, 2, 3]。但是,当执行 arr = [4, 5, 6]; 时,arr 被重新赋值,指向了一个新的数组 [4, 5, 6]。
需要注意的是,这种赋值不会影响到 newArr,因为 newArr 依旧保持指向原来的数组 [1, 2, 3]。简单来说,arr 重新指向了一个新的对象,而 newArr 还在指向原来的数组。
对象引用与原始数据的区别
![]()
在 JavaScript 中,对象、数组等复杂数据类型的变量并不直接保存数据的值,而是保存引用。当我们对变量重新赋值时,我们只是改变了它指向的内存地址,而原来的引用仍然有效。这也是为什么在打印 newArr 时,它依旧指向 [1, 2, 3]。
重新赋值与直接修改的差异
![]()
如果我们希望改变 newArr 也能看到新数组的变化,就不能直接给 arr 重新赋值,而是需要修改数组本身的内容,比如:
arr.push(4);
这样,arr 和 newArr 都会看到新的内容。
💯场景三:修改数组内容
最后,我们来看第三个代码片段:
var arr = [1, 2, 3];
var newArr = arr;
arr[2] = 6;
console.log(newArr); // 输出是什么?
运行结果:
[1, 2, 6]

为什么结果变成了 [1, 2, 6]?
![]()
在这里,我们需要理解的一个重要概念是“修改数组的内容”和“重新赋值”的区别。
var arr = [1, 2, 3];创建了一个数组,并将其引用赋值给arr。var newArr = arr;将arr的引用赋值给newArr,此时arr和newArr都指向同一个数组。arr[2] = 6;直接修改了数组的第三个元素。
由于 arr 和 newArr 都指向相同的数组,这意味着对数组内容的任何更改对这两个变量都是可见的。因此,当 arr[2] 被修改为 6 时,newArr 看到的也是修改后的数组 [1, 2, 6]。
JavaScript 中的内存共享
![]()
在 JavaScript 中,数组和对象是通过引用来传递的。当多个变量引用同一个数组时,修改这个数组的内容将影响到所有引用该数组的变量。这种行为称为内存共享。
要理解内存共享,可以将数组或对象看作是存在于某个位置的数据块,而变量是指向这个数据块的“指针”。当我们通过一个变量修改数据块时,所有引用这个数据块的变量都会反映出相应的变化。

💯深入理解 JavaScript 的内存模型与赋值行为
![]()
为了更好地理解上述三种情况,我们还需要进一步了解 JavaScript 的内存管理和变量赋值行为。
1. JavaScript 中的值类型和引用类型
![]()
JavaScript 中的数据类型分为两类:
- 基本数据类型(值类型):包括
Number、String、Boolean、Null、Undefined、Symbol、BigInt。这些类型的数据是按值传递的,这意味着每个变量都存储数据的副本。 - 引用数据类型:包括
Object、Array、Function等。这些类型的数据是按引用传递的,变量保存的是对象的内存地址,而不是对象本身。
对于基本数据类型,变量赋值是直接复制值的副本,因此两个变量之间不会互相影响。对于引用类型,变量保存的是对象在内存中的地址,两个引用指向相同的地址意味着它们共享相同的内存内容。
2. 内存分配与垃圾回收
![]()
JavaScript 的内存分为两种主要区域:
- 栈内存(Stack Memory):用于存储基本类型的值和引用类型的引用。
- 堆内存(Heap Memory):用于存储引用类型的实际内容(对象、数组等)。
当执行赋值操作 var newArr = arr 时,newArr 和 arr 都指向堆内存中的同一个数组对象,因此对数组内容的修改对这两个变量来说是可见的。而当重新赋值 arr = [4, 5, 6] 时,arr 被重新赋予了一个新的引用,因此它和 newArr 分道扬镳。
💯如何避免引用带来的问题
![]()
在实际开发中,共享引用数据类型可能会带来一些不可预见的副作用,因此有时我们希望克隆数组或对象,以避免修改对其他变量产生影响。
1. 浅拷贝与深拷贝
![]()
浅拷贝 只复制对象的第一层引用,而 深拷贝 会递归复制所有嵌套的对象和数组。
-
浅拷贝的方法:使用
Object.assign()或展开运算符...。var arr = [1, 2, 3]; var shallowCopy = [...arr]; // 浅拷贝 shallowCopy[0] = 9; console.log(arr); // [1, 2, 3] console.log(shallowCopy); // [9, 2, 3] -
深拷贝的方法:使用
JSON.parse(JSON.stringify(obj))(这种方式有局限性,无法拷贝函数和某些特殊对象)。var obj = { a: 1, b: { c: 2 } }; var deepCopy = JSON.parse(JSON.stringify(obj)); deepCopy.b.c = 9; console.log(obj.b.c); // 2 console.log(deepCopy.b.c); // 9
2. 使用 Object.assign 或 lodash.cloneDeep
![]()
Object.assign() 可以用来实现对象的浅拷贝,而 lodash 库提供了一个更强大的深拷贝方法 _.cloneDeep(),可以递归地复制嵌套的对象和数组。
💯小结

在 JavaScript 中,理解变量赋值、引用以及内存模型对于掌握语言的行为至关重要。在本文中,我们详细探讨了三种代码场景,并通过对比分析深入理解了以下几点:
- 变量赋值与引用:赋值为引用数据类型时,变量保存的是内存地址,而不是数据本身。因此,重新赋值并不会影响其他引用该数据的变量。
- 内存模型:JavaScript 中,栈内存用于存储基本类型和引用地址,而堆内存用于存储复杂对象的内容。对引用对象的操作会影响到所有指向该内存地址的变量。
- 修改数组内容与重新赋值:直接修改数组的内容会影响所有引用该数组的变量,而重新赋值则会让变量指向一个新的对象,不影响其他引用。

相关文章:
【前端】JavaScript 变量引用、内存与数组赋值:深入解析三种情景
博客主页: [小ᶻZ࿆] 本文专栏: 前端 文章目录 💯前言💯场景一:直接赋值与重新引用为什么结果不是 [3, 4, 5]?1. 引用与赋值的基本概念2. 图示分析 关键总结 💯场景二:引用指向的变化为什么…...
本地项目运行提示跨域问题
项目背景:我使用phpwebstudy在本地搭建了一个项目,然后前端是http://localhost:8080/ 后端我直接创建了一个本地域名,例如www.abc.com 然后vue.config.js配置如下,这个配置在我所有线上环境是没有任何问题的 devServer: {proxy…...
C++ —— string类(上)
目录 string的介绍 string类功能的使用介绍 constructor —— 构造 介绍使用(1)(2)(4) :构造、拷贝构造、带参构造 介绍(3):拷贝string类对象的一部分字符…...
React Native Mac 环境搭建
下载 Mac 版Android Studio 下载 安装 JDK 环境 Flutter 项目实战-环境变量配置一 安装 Node.js 方式一 通过Node.js 官网下载 下载完成后点击安装包进行安装 安装完成...
Python Web 开发的路径管理艺术:FastAPI 项目中的最佳实践与问题解析20241119
Python Web 开发的路径管理艺术:FastAPI 项目中的最佳实践与问题解析 引言:从路径错误到模块化管理的技术旅程 在现代 Python Web 开发中,路径管理是一个常常被忽视却非常重要的问题。尤其是在使用像 FastAPI 和 Tortoise ORM 这样的框架时…...
Rust derive macro(Rust #[derive])Rust派生宏
参考文章:附录 D:派生特征 trait 文章目录 Rust 中的派生宏 #[derive]基础使用示例:派生 Debug 派生其他常用特征示例:派生 Clone 和 Copy 派生宏的限制和自定义派生自定义派生宏上面代码运行时报错了,以下是解释 结论…...
springboot嗨玩旅游网站
摘 要 嗨玩旅游网站是一个专为旅行爱好者打造的在线平台。我们提供丰富多样的旅游目的地信息,包括景点信息、旅游线路、商品信息、社区信息、活动推广等,帮助用户轻松规划行程。嗨玩旅游网站致力于为用户提供便捷、实用的旅行服务,让每一次旅…...
杰发科技AC7840——EEP中RAM的配置
sample和手册中示例代码的sram区地址定义不一样 这个在RAM中使用没有限制,根据这个表格留下足够空间即可 比如需要4096字节的eep空间,可以把RAM的地址改成E000,即E000-EFFF,共4096bytes即可。...
从零开始的c++之旅——map_set的使用
1.序列式容器和关联式容器 序列式容器:逻辑结构为线性序列的数据结构,两个位置之间没有紧密的关系,比如两者交换一下还是序列式的容器,例如string,vector,deque,array等。 关联式容器࿱…...
Docker中的一些常用命令
find / -type f -name “文件名” 2>/dev/null 寻找所有目录中的这个文件 pwd 查看当前目录的地址 docker pull 镜像名 强制拉镜像 docker run 运行docker systemctl daemon-reload 关闭docker systemctl start docker 启动docker systemctl restart docker 重启docker /…...
自存 sql常见语句和实际应用
关于连表 查询两个表 SELECT * FROM study_article JOIN study_article_review 查询的就是两个表相乘,结果为两个表的笛卡尔积 相这样 这种并不是我们想要的结果 通常会添加一些查询条件 SELECT * FROM study_articleJOIN study_article_review ON study_art…...
python | argparse模块在命令行的使用中的重要作用
import argparseclass TestCases:def __init__(self, nameNone, expect_resultNone):self.name nameself.expect expect_resultself.parser argparse.ArgumentParser() # 创建命令解析器self.add_arguments() # 方法 : 添加命令self.args, _ self.parser.par…...
【HCIP]——OSPF综合实验
题目 实验需求 根据上图可得,实验需求为: 1.R5作为ISP:其上只能配置IP地址;R4作为企业边界路由器,出口公网地址需要通过PPP协议获取,并进行CHAP认证。(PS:因PPP协议尚未学习&#…...
PW系列工控电脑复制机:效率与精度双重提升
工控电脑复制应用:效率与精度的双重提升 随着现代企业对大数据、数据备份、和跨平台兼容性需求的快速增长,工控电脑已成为数据密集型产业的核心设备。针对工控环境中大量数据复制的特殊需求,PW系列NVMe/SATA PCIe SSD复制机(如PW…...
学习QT第二天
QT6示例运行 运行一个Widgets程序运行一个QT Quick示例 工作太忙了,难得抽空学点东西。-_-||| 博客中有错误的地方,请各位道友及时指正,感谢! 运行一个Widgets程序 在QT Creator的欢迎界面中,点击左侧的示例…...
11.20作业
题目一: 题目: // 数组的行列转置 代码: // 数组的行列转置 #include <stdio.h> int main() {int a[2][3], i, j, b[3][2];printf("输入一个两行三列的数组a:\n");for (i 0; i < 2; i)for (j 0; j < 3; j){scanf…...
Ubuntu Linux使用前准备动作_使用root登录图形化界面
Ubuntu默认是不允许使用 root 登录图形化界面的。这是出于安全考虑的设置。但如果有需要,可以通过以下步骤来实现使用 root 登录: 1、设置 root 密码 打开终端,使用当前的管理员账户登录系统。在终端中输入命令sudo passwd root,…...
DICOM核心概念:显式 VR(Explicit VR)与隐式 VR(Implicit VR)在DICOM中的定义与区别
在DICOM(Digital Imaging and Communications in Medicine)标准中,VR(Value Representation) 表示数据元素的值的类型和格式。理解显式 VR(Explicit VR)与隐式 VR(Implicit VR&#…...
源码分析Spring Boot (v3.3.0)
. ____ _ __ _ _/\\ / ____ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | _ | _| | _ \/ _ | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) ) |____| .__|_| |_|_| |_\__, | / / / /|_||___//_/_/_/:: Spring Boot :: (v3.3.0)//笔记背…...
IPv6 NDP 记录
NDP(Neighbor Discovery Protocol,邻居发现协议) 是 IPv6 的一个关键协议,它组合了 IPv4 中的 ARP、ICMP 路由器发现和 ICMP 重定向等协议,并对它们作出了改进。该协议使用 ICMPv6 协议实现,作为 IPv6 的基…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...
Pydantic + Function Calling的结合
1、Pydantic Pydantic 是一个 Python 库,用于数据验证和设置管理,通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发(如 FastAPI)、配置管理和数据解析,核心功能包括: 数据验证:通过…...
云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...
sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...
多元隐函数 偏导公式
我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式,给定一个隐函数关系: F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 🧠 目标: 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z、 …...
