JavaScript - 通过引用传递时清空数组/对象问题

Posted

技术标签:

【中文标题】JavaScript - 通过引用传递时清空数组/对象问题【英文标题】:JavaScript - Empty an Array / Object while Passing by Reference Issue 【发布时间】:2022-01-21 04:20:53 【问题描述】:

据我了解,数组和对象都是基于内存地址的按引用传递,所以如果我创建另一个变量并指向数组/对象,并改变任何值,另一个值也应该改变。

但是,我不太明白它在这里是如何工作的。我指向array1并修改array1为空,为什么另一个数组的值没有改变?

   var array1 = [1,2,3,4,5,6,7];  // Created array
   var anotherArray = array1;     // Referenced array1 by another variable
   array1 = [];                   // Empty the array
   console.log(anotherArray);  // Output [1,2,3,4,5,6,7]

我可以理解下面的例子为什么anotherArray变成[]空,因为它是通过引用传递的,但是为什么anotherArray仍然输出[1,2,3,4,5,6,7]?

   var array1 = [1,2,3,4,5,6,7]; // Created array
   var anotherArray = array1; // Referenced array1 by another variable
   array1.length = 0; // Empty the array by setting length to 0
   console.log(anotherArray); // Output []

谢谢。

【问题讨论】:

在第一个示例中,您没有修改array1 所指的数组,而是重新分配 array1 所指的数组。在第二个示例中,您正在修改 array1 引用的数组 在上面的例子中重新分配和修改有什么区别?谢谢 为了存在关系,两个数组应该共存。如果您删除或重新定义其中之一,那么关系就会变得膨胀.. JS 中没有引用传递,一切都是值传递。碰巧对于对象/数组,值 itself 是一个引用。当您执行array1 = [] 时,您只是在替换值。 【参考方案1】:

赋值给一个变量从不改变该变量之前作为值的对象。

对象——比如数组——只有在你改变它们时才会改变,要么通过调用实现这一点的方法,要么通过设置属性。

这是您的第一个脚本的可视化:

var array1 = [1,2,3,4,5,6,7] 结果如下:

  array1
   ↓
  ┌──────┬───┬───┬───┬───┬───┬───┬───┐
  │length│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │
  ├──────┼───┼───┼───┼───┼───┼───┼───┤
  │  7   │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
  └──────┴───┴───┴───┴───┴───┴───┴───┘

var anotherArray = array1 之后我们有这个:

  array1
   ↓
  ┌──────┬───┬───┬───┬───┬───┬───┬───┐
  │length│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │
  ├──────┼───┼───┼───┼───┼───┼───┼───┤
  │  7   │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
  └──────┴───┴───┴───┴───┴───┴───┴───┘
   ↑
  anotherArray

然后array1 = [] 将创建一个新数组:所以现在有两个数组。它还使array1 成为新创建数组的引用:

  array1
   ↓
  ┌──────┐
  │length│
  ├──────┤
  │  0   │
  └──────┘

  ┌──────┬───┬───┬───┬───┬───┬───┬───┐
  │length│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │
  ├──────┼───┼───┼───┼───┼───┼───┼───┤
  │  7   │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
  └──────┴───┴───┴───┴───┴───┴───┴───┘
   ↑
  anotherArray

在第二个脚本中,分配给length 属性(实际上是一个setter)将改变 数组。没有创建新数组,1,2,3,4,5,6,7 的值都丢失了:

  array1
   ↓
  ┌──────┐
  │length│
  ├──────┤
  │  0   │
  └──────┘
   ↑
  anotherArray

【讨论】:

【参考方案2】:

对于您的第一个示例,您实际上是将 array1 重新分配为一个空数组(现在它们指向内存中的 2 个不同的数组),因此 anotherArray 保持不变。

 var array1 = [1,2,3,4,5,6,7];  // Created array
 var anotherArray = array1;     // Referenced array1 by another variable
 array1 = [];                   // Empty the array
 console.log(anotherArray);  // Output [1,2,3,4,5,6,7]

在这种情况下,您实际上是通过将数组的长度分配为 0 来修改数组。由于 array1 引用它,它也会影响他,因为它们引用内存中的同一个数组。

  var array1 = [1,2,3,4,5,6,7]; // Created array
  var anotherArray = array1; // Referenced array1 by another variable
  array1.length = 0; // Empty the array by setting length to 0
  console.log(anotherArray); // Output []

【讨论】:

【参考方案3】:

线

array1 = [];

修改先前分配给array1 的数组对象。它分配一个新的(空)数组给变量;这不像对分配给 array1 的对象调用操作。

换句话说,假设array1 是唯一引用先前分配的数组对象([1,2,3,4,5,6,7])的变量。执行该行(这是一个语句,顺便说一句)后,您丢失了先前分配的数组;一段时间后它会被垃圾收集。这都是因为这里使用了 赋值运算符,并且它们的操作语义是在 javascript 中定义的。

更重要的是,您在这里误用了按引用传递的概念(又名按引用调用);这是一个与值如何传递给函数调用(或子例程/过程,如果您更喜欢谈论子例程/过程而不是函数)的参数有关的概念。

【讨论】:

【参考方案4】:

在第一个实例中,您实际上并没有更新 array1 引用的对象。 您正在替换变量名称array1 下的值。 anotherArray 指向内存中的值,该值是 array1 中保存的先前值。

在第二个示例中,您实际上是在更新 array1anotherArray 都指向的基础值

【讨论】:

【参考方案5】:

在assignment operator、a = x 中,右侧被评估为表达式 (x),结果为分配给a

这意味着 a 永远不会“引用”x

因此a = ba = [] 导致a 被重新分配。如果右侧是变量(被评估)或空数组文字,则无关紧要:

var b = [1,2,3] // create and assign array #1
var a = b
// ^— a is assigned the result of evaluating b
// after this assignment a and b “refer to” (read: evaluate to) the same object..
// b -> [1,2,3] #1
// a -> [1,2,3] #1

a.pop()
// ^— modify array object (a.length = 0 would empty the array)
// ..and since a and b refer to THE SAME ARRAY..
// b -> [1,2] #1
// a -> [1,2] #1

var a = [] // create and assign array #2
// ^— a is assigned the result of evaluating []
// after re-assignment both a and b refer to different array objects..
// (the assignment did not alter mutate the original array)
// b -> [1,2] #1
// a -> []    #2

a.push(3)
// ^— modify array object
// ..and since a and b refer to DIFFERENT ARRAYS..
// b -> [1,2] #1
// a -> [3]   #2

调用函数的工作方式与赋值完全相同JavaScript 中没有“引用调用”表达式的求值产生的作为参数提供。 JavaScript 具有Call by (Object) Sharing 语义。

tldr:这里不需要讨论 Javascript 中的“引用”,因为它们会增加不必要的混淆,甚至在规范中(以这种方式)都没有定义!

【讨论】:

以上是关于JavaScript - 通过引用传递时清空数组/对象问题的主要内容,如果未能解决你的问题,请参考以下文章

如何在JavaScript中清空数组? [重复]

Javascript:通过引用传递对象

PHP是不是优化数组类型的函数参数,而不是通过引用显式传递,当它们没有被修改时?

Swift:通过引用传递数组?

使用通过引用传递的字符串数组到运行时加载的 dll 函数

JavaScript 是不是通过引用传递? [复制]