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

news2024/11/23 15:31:54

在这里插入图片描述

博客主页: [小ᶻZ࿆]
本文专栏: 前端

文章目录

  • 💯前言
  • 💯场景一:直接赋值与重新引用
    • 为什么结果不是 `[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 中,基本数据类型(如 numberstring 等)是按值传递的,而复杂数据类型(如数组、对象)是按引用传递的。

在代码中,var arr = [1, 2, 3] 创建了一个数组 [1, 2, 3],并且将其引用赋值给变量 arr。此时,arr 保存的是数组在内存中的引用(地址),而不是数组的值本身。

然后,执行 var newArr = arr;,这意味着 newArr 保存了与 arr 相同的引用。也就是说,newArrarr 都指向了相同的内存地址,这个内存中存储的是数组 [1, 2, 3]

当我们执行 newArr = [3, 4, 5]; 时,newArr 被重新赋值,指向了一个新的数组 [3, 4, 5] 的内存地址。此时,newArr 不再指向原来的数组 [1, 2, 3],而是指向了一个全新的数组。而 arr 依然保持指向原始数组的引用,因此打印 arr 时结果仍然是 [1, 2, 3]


2. 图示分析

在这里插入图片描述

  • 初始状态:

    • arrnewArr 都指向 [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; 之后,newArrarr 都指向同一个数组 [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);

这样,arrnewArr 都会看到新的内容。


💯场景三:修改数组内容

最后,我们来看第三个代码片段:

var arr = [1, 2, 3];
var newArr = arr;
arr[2] = 6;
console.log(newArr); // 输出是什么?

运行结果:

[1, 2, 6]

在这里插入图片描述


为什么结果变成了 [1, 2, 6]

在这里插入图片描述

在这里,我们需要理解的一个重要概念是“修改数组的内容”和“重新赋值”的区别。

  1. var arr = [1, 2, 3]; 创建了一个数组,并将其引用赋值给 arr
  2. var newArr = arr;arr 的引用赋值给 newArr,此时 arrnewArr 都指向同一个数组。
  3. arr[2] = 6; 直接修改了数组的第三个元素。

由于 arrnewArr 都指向相同的数组,这意味着对数组内容的任何更改对这两个变量都是可见的。因此,当 arr[2] 被修改为 6 时,newArr 看到的也是修改后的数组 [1, 2, 6]


JavaScript 中的内存共享

在这里插入图片描述

在 JavaScript 中,数组和对象是通过引用来传递的。当多个变量引用同一个数组时,修改这个数组的内容将影响到所有引用该数组的变量。这种行为称为内存共享。

要理解内存共享,可以将数组或对象看作是存在于某个位置的数据块,而变量是指向这个数据块的“指针”。当我们通过一个变量修改数据块时,所有引用这个数据块的变量都会反映出相应的变化。
在这里插入图片描述


💯深入理解 JavaScript 的内存模型与赋值行为

在这里插入图片描述

为了更好地理解上述三种情况,我们还需要进一步了解 JavaScript 的内存管理和变量赋值行为。


1. JavaScript 中的值类型和引用类型

在这里插入图片描述

JavaScript 中的数据类型分为两类:

  • 基本数据类型(值类型):包括 NumberStringBooleanNullUndefinedSymbolBigInt。这些类型的数据是按值传递的,这意味着每个变量都存储数据的副本。
  • 引用数据类型:包括 ObjectArrayFunction 等。这些类型的数据是按引用传递的,变量保存的是对象的内存地址,而不是对象本身。

对于基本数据类型,变量赋值是直接复制值的副本,因此两个变量之间不会互相影响。对于引用类型,变量保存的是对象在内存中的地址,两个引用指向相同的地址意味着它们共享相同的内存内容。


2. 内存分配与垃圾回收

在这里插入图片描述

JavaScript 的内存分为两种主要区域:

  • 栈内存(Stack Memory):用于存储基本类型的值和引用类型的引用。
  • 堆内存(Heap Memory):用于存储引用类型的实际内容(对象、数组等)。

当执行赋值操作 var newArr = arr 时,newArrarr 都指向堆内存中的同一个数组对象,因此对数组内容的修改对这两个变量来说是可见的。而当重新赋值 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.assignlodash.cloneDeep

在这里插入图片描述

Object.assign() 可以用来实现对象的浅拷贝,而 lodash 库提供了一个更强大的深拷贝方法 _.cloneDeep(),可以递归地复制嵌套的对象和数组。


💯小结

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

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2244445.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序的医院预约挂号系统

摘 要 时代在飞速进步,每个行业都在努力发展现在先进技术,通过这些先进的技术来提高自己的水平和优势,医院预约挂号系统当然不能排除在外。医院预约挂号系统是在实际应用和软件工程的开发原理之上,运用微信开发者、java语言以及S…

微店商品详情 API 接口如何获取

要获取微店商品详情API接口,你需要遵循以下步骤: 注册与认证:首先,你需要在微店平台完成注册,并进行必要的认证流程,以确保具备获取API接口的资格。 深入研究政策:仔细阅读并理解微店关于API使…

新华三H3CNE网络工程师认证—子接口技术

子接口(subinterface)是通过协议和技术将一个物理接口(interface)虚拟出来的多个逻辑接口。在VLAN虚拟局域网中,通常是一个物理接口对应一个 VLAN。在多个 VLAN 的网络上,无法使用单台路由器的一个物理接口…

matlab例题

matlab中,abs函数是求绝对值的函数,但是他也能求字符串的ascill值

Android 工厂设计模式的使用:咖啡机,可以做拿铁,可以做美式等等。

大家好呀~,我是前期后期,在网上冲浪的一名程序员,分享一些自己学到的知识,希望能够帮助大家节省时间。 目录 一、为什么使用:问题是什么 我们有一款纸币器,但是纸币器有三种通讯方式:ICT RS32、…

【汇编语言】转移指令的原理(三) —— 汇编跳转指南:jcxz、loop与位移的深度解读

文章目录 前言1. jcxz 指令1.1 什么是jcxz指令1.2 如何操作 2. loop 指令2.1 什么是loop指令2.2 如何操作 3. 根据位移进行转移的意义3.1 为什么?3.2 举例说明 4. 编译器对转移位移超界的检测结语 前言 📌 汇编语言是很多相关课程(如数据结构…

django启动项目报错解决办法

在启动此项目报错: 类似于: django.core.exceptions.ImproperlyConfigured: Requested setting EMOJI_IMG_TAG, but settings are not c启动方式选择django方式启动,以普通python方式启动会报错 2. 这句话提供了对遇到的错误的一个重要线索…

为什么 Vue3 封装 Table 组件丢失 expose 方法呢?

在实际开发中,我们通常会将某些常见组件进行二次封装,以便更好地实现特定的业务需求。然而,在封装 Table 组件时,遇到一个问题:Table 内部暴露的方法,在封装之后的组件获取不到。 代码展示为: …

项目-摄像

树莓派摄像头使用方法 Camera教程 https://www.raspi.cc/index.php?cread&id53&page1 nanopc-t4 ​https://www.raspi.cc/index.php?cread&id53&page1 摄像头型号 Raspberry Pi Camera Rev 1.3 检测故障 dmesg | grep -i mipi piNanoPC-T4:~$ dmesg | …

【ArcGIS微课1000例】0127:计算城市之间的距离

本文讲述,在ArcGIS中,计算城市(以地级城市为例)之间的距离,效果如下图所示: 一、数据准备 加载配套实验数据包中的地级市和行政区划矢量数据(订阅专栏后,从私信查收数据),如下图所示: 二、计算距离 1. 计算邻近表 ArcGIS提供了计算点和另外点之间距离的工具:分析…

解决python import ddddocr报错DLL load failed while...的办法

目录 1. DLL1.1 DLL是什么1.2 为什么会出现这个 2. 如何解决2.1 安装Microsoft Visual C Redistributable推荐</sup>2.2 安装或更新onnxruntime库2.3 检查环境变量 在Python中导入ddddocr模块的时候遇到“DLL load failed”错误, 所以记录一下解决过程。 1. DLL 1.1 DL…

All-in-one Notion 介绍

Notion 是一款集笔记、知识管理、任务规划和协作于一体的工具&#xff0c;它以高度的灵活性和可自定义的工作空间而闻名。它适合个人和团队使用&#xff0c;能够帮助用户高效管理生活、学习和工作。以下是 Notion 的一些主要特点&#xff1a; 1. 多功能工作区 Notion 将笔记、…

Linux-第2集-打包压缩 zip、tar WindowsLinux互传

欢迎来到Linux第2集&#xff0c;这一集我会非常详细的说明如何在Linux上进行打包压缩操作&#xff0c;以及解压解包 还有最最重要的压缩包的网络传输 毕竟打包压缩不是目的&#xff0c;把文件最终传到指定位置才是目的 由于打包压缩分开讲没有意义&#xff0c;并且它们俩本来…

Unity脚本基础规则

Unity脚本基础规则 如何在Unity中创建一个脚本文件&#xff1f; 在Project窗口中的Assets目录下&#xff0c;选择合适的文件夹&#xff0c;右键&#xff0c;选择第一个Create&#xff0c;在新出现的一栏中选择C# Script&#xff0c;此时文件夹内会出现C#脚本图标&#xff0c;…

[Qt platform plugin问题] Could not load the Qt platform plugin “xcb“

Qt platform plugin 是 Qt 应用程序启动时加载的插件。不同的平台有不同的插件。 常见的插件有:linuxfb Wayland xcb 简单来说就是启动一个GUI程序, 离不开这些插件.选择其中一个就好 出现这个问题要么就是没有插件&#xff0c;要么就是插件依赖的库没有。 要么就是插件选则的…

MySql 索引视图存储变量

要求 一&#xff1a; 学生表:Student(Sno&#xff0c;Sname&#xff0c;Ssex &#xff0c;Sage, Sdept) 学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 Sno为主键 课程表:Course(Cno&#xff0c;Cname) 课程号&#xff0c;课程名 Cno为主键 学生…

高阶云服务-ELB+AS

ELBAS 弹性负载均衡弹性伸缩 原来1台web服务器不满足相应&#xff0c;现部署多台提供相同服务&#xff1b; 由于多个服务器多个ip该如何提供给应用呢&#xff1f; 引申出负载均衡&#xff08;HAProxy&#xff0c;LVS01四层&#xff0c;Nginx七层&#xff09; 防单点故障做主备…

集群聊天服务器(13)redis环境安装和发布订阅命令

目录 环境安装订阅redis发布-订阅的客户端编程环境配置客户端编程 功能测试 环境安装 sudo apt-get install redis-server 先启动redis服务 /etc/init.d/redis-server start默认在6379端口上 redis是存键值对的&#xff0c;还可以存链表、数组等等复杂数据结构 而且数据是在…

SpringBoot 集成 Sharding-JDBC(一):数据分片

在深入探讨 Sharding-JDBC 之前&#xff0c;建议读者先了解数据库分库分表的基本概念和应用场景。如果您还没有阅读过相关的内容&#xff0c;可以先阅读我们之前的文章&#xff1a; 关系型数据库海量数据存储策略-CSDN博客 这篇文章将帮助您更好地理解分库分表的基本原理和实现…

芋道启用undertown

芋道使用的是tomcat, 可以替换为性能更好的undertown 1.maven配置 1.1在yudao-dependencies模块下yudao-spring-boot-starter-web 和yudao-spring-boot-starter-websocket排除spring-boot-starter-tomcat 添加 <exclusions><exclusion><groupId>org.spring…