【前端】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 的基…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...
海云安高敏捷信创白盒SCAP入选《中国网络安全细分领域产品名录》
近日,嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》,海云安高敏捷信创白盒(SCAP)成功入选软件供应链安全领域产品名录。 在数字化转型加速的今天,网络安全已成为企业生存与发展的核心基石,为了解…...
Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合
无论是python,或者java 的大型项目中,都会涉及到 自身平台微服务之间的相互调用,以及和第三发平台的 接口对接,那在python 中是怎么实现的呢? 在 Python Web 开发中,FastAPI 和 Django 是两个重要但定位不…...
如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
接口 RESTful 中的超媒体:REST 架构的灵魂驱动
在 RESTful 架构中,** 超媒体(Hypermedia)** 是一个核心概念,它体现了 REST 的 “表述性状态转移(Representational State Transfer)” 的本质,也是区分 “真 RESTful API” 与 “伪 RESTful AP…...
