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
中保存的先前值。
在第二个示例中,您实际上是在更新 array1
和 anotherArray
都指向的基础值
【讨论】:
【参考方案5】:在assignment operator、a = x
中,右侧被评估为表达式 (x
),结果值为分配给a
。
这意味着 a
永远不会“引用”x
。
因此a = b
和a = []
导致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 - 通过引用传递时清空数组/对象问题的主要内容,如果未能解决你的问题,请参考以下文章