首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【前端】JavaScript 变量引用、内存与数组赋值:深入解析三种情景

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

作者头像
CSDN-Z
发布2024-11-21 17:33:39
发布2024-11-21 17:33:39
44200
代码可运行
举报
文章被收录于专栏:AIGCAIGC
运行总次数:0
代码可运行

💯前言

  • JavaScript 是一种基于对象的脚本语言,常用于前端开发。初学者在使用 JavaScript 时,通常会遇到一些关于变量引用和赋值的困惑。本文将详细讨论三种不同的代码场景,结合 JavaScript 的变量引用与内存模型,深入分析为什么这些代码输出会如此不同。希望通过对这些原理的探讨,能够帮助您更好地理解 JavaScript 中的变量引用机制。

💯场景一:直接赋值与重新引用

首先,我们来看以下的代码片段:

代码语言:javascript
代码运行次数:0
运行
复制
var arr = [1, 2, 3];
var newArr = arr;
newArr = [3, 4, 5];
console.log(arr); // 输出是什么?

运行结果:

代码语言:javascript
代码运行次数:0
运行
复制
[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 中,给一个变量赋予一个新的数组时,并不会改变原来的数组,而是创建了一个新的引用。如果希望改变所有引用同一数组的变量,那么需要对数组本身进行修改,而不是重新赋值。

💯场景二:引用指向的变化

接下来看第二个代码片段:

代码语言:javascript
代码运行次数:0
运行
复制
var arr = [1, 2, 3];
var newArr = arr;
arr = [4, 5, 6];
console.log(newArr); // 输出是什么?

运行结果:

代码语言:javascript
代码运行次数:0
运行
复制
[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 重新赋值,而是需要修改数组本身的内容,比如:

代码语言:javascript
代码运行次数:0
运行
复制
arr.push(4);

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


💯场景三:修改数组内容

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

代码语言:javascript
代码运行次数:0
运行
复制
var arr = [1, 2, 3];
var newArr = arr;
arr[2] = 6;
console.log(newArr); // 输出是什么?

运行结果:

代码语言:javascript
代码运行次数:0
运行
复制
[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() 或展开运算符 ...

代码语言:javascript
代码运行次数:0
运行
复制
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))(这种方式有局限性,无法拷贝函数和某些特殊对象)。

代码语言:javascript
代码运行次数:0
运行
复制
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. 修改数组内容与重新赋值:直接修改数组的内容会影响所有引用该数组的变量,而重新赋值则会让变量指向一个新的对象,不影响其他引用。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 💯前言
  • 💯场景一:直接赋值与重新引用
    • 为什么结果不是 [3, 4, 5]?
      • 1. 引用与赋值的基本概念
      • 2. 图示分析
    • 关键总结
  • 💯场景二:引用指向的变化
    • 为什么结果还是 [1, 2, 3]?
    • 对象引用与原始数据的区别
    • 重新赋值与直接修改的差异
  • 💯场景三:修改数组内容
    • 为什么结果变成了 [1, 2, 6]?
    • JavaScript 中的内存共享
  • 💯深入理解 JavaScript 的内存模型与赋值行为
    • 1. JavaScript 中的值类型和引用类型
    • 2. 内存分配与垃圾回收
  • 💯如何避免引用带来的问题
    • 1. 浅拷贝与深拷贝
    • 2. 使用 Object.assign 或 lodash.cloneDeep
  • 💯小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档