任何人都可以向我解释JavaScript中的这种奇怪行为吗?我以为我理解传递引用和值传递是如何工作的,但是JavaScript似乎在如何处理它。两个简单的例子:
var arr = [1, 2, 3];
function reassign(array) {
array = [1, 1, 1];
}
reassign(arr);
console.log(arr) // [1, 2, 3]
var arr2 = [1, 2, 3];
function withSplice(array) {
array.splice(1, 1);
}
withSplice(arr2);
console.log(arr2) // [1, 3]
我的思维过程在哪里失败?我觉得重新分配'函数正在接收数组的副本,因此重新分配对原始文件没有任何作用,但随后是' withSplice'方法似乎收到了实际的数组。是什么赋予了?提前谢谢!
答案 0 :(得分:6)
为什么数组有时会被值传递而不是其他数据?
它没有。在两种情况下,数组的引用都会(按值)传递给函数。在您的第一个代码段中,然后替换 array
变量中的引用,并引用新数组; array
变量与您的arr
变量无关,后者一直引用旧数组。在第二个代码段中,您修改了array
和arr
所指的原始数组的状态。
(更多关于术语"按值传递"和"传递参考",具有非常具体的含义,如下所示。)
让我们退一步,然后我们会看看你的片段。
在JavaScript中(与许多语言一样),变量只包含原始值,例如(例如)数字15.一种原始值是对数组或其他类型对象的引用,它存在于变量的内存中。这些引用是我们从未直接看到的值,它们告诉JavaScript引擎在内存中的其他位置找到数组/对象。
所以说我们有这个代码:
var n = 15;
var a = [1, 2, 3];
这里有一些我们在记忆中得到的ASCII艺术:
+−−−−−+ | n | +−−−−−+ | 15 | +−−−−−+ +−−−−−−−−−−−−−−+ | a | +−−−+ +−−−−−−−−−−−−−−+ | 1 | | (ref #123) |−−−−>| 2 | +−−−−−−−−−−−−−−+ | 3 | +−−−+
n
包含值15; a
包含指向数组的对象引用,内存中的其他位置。
当您将变量的值传递给函数(或将其分配给另一个变量等)时,会生成其中原始值的副本。在对象引用的情况下复制的值是对象引用;然后,引用的两个副本都引用同一个对象。所以,如果我们这样做:
var m = n;
var b = a;
我们得到:
+−−−−−+ | n | +−−−−−+ | 15 | +−−−−−+ +−−−−−+ | m | +−−−−−+ | 15 | +−−−−−+ +−−−−−−−−−−−−−−+ | a | +−−−−−−−−−−−−−−+ | (ref #123) |−−+ +−−−−−−−−−−−−−−+ | +−−−+ | | 1 | +−>| 2 | +−−−−−−−−−−−−−−+ | | 3 | | b | | +−−−+ +−−−−−−−−−−−−−−+ | | (ref #123) |−−+ +−−−−−−−−−−−−−−+
在这两种情况下,变量的值都被复制到一个新变量。
那么你的片段中发生了什么?
在第一个代码段中,您为array
变量(参数)提供了一个 new 值,即对新数组的引用,与原始数组分开。由于array
变量与arr
变量没有关联,arr
仍然引用原始数组。
在第二个代码段中,您要更改原始数组的状态,而不是array
变量的值。由于array
和arr
都引用同一个数组,因此无论您使用哪个引用副本查看它,都会看到修改后的状态。
更多ASCII-art:
在您的第一个代码段中,您将从内存开始:
+−−−−−−−−−−−−+ | arr | +−−−+ +−−−−−−−−−−−−+ | 1 | | (ref #123) |−−−−>| 2 | +−−−−−−−−−−−−+ | 3 | +−−−+
然后,当您在reassign
行之前输入array = [1, 1, 1];
函数时,您有:
+−−−−−−−−−−−−+ | arr | +−−−−−−−−−−−−+ | (ref #123) |−−+ +−−−−−−−−−−−−+ | +−−−+ | | 1 | +−>| 2 | +−−−−−−−−−−−−+ | | 3 | | array | | +−−−+ +−−−−−−−−−−−−+ | | (ref #123) |−−+ +−−−−−−−−−−−−+
每个变量都包含对数组的引用的副本。
然后 array = [1, 1, 1];
函数中的reassign
行,但在它返回之前,您有:
+−−−−−−−−−−−−+ | arr | +−−−+ +−−−−−−−−−−−−+ | 1 | | (ref #123) |−−−−>| 2 | +−−−−−−−−−−−−+ | 3 | +−−−+ +−−−−−−−−−−−−+ | array | +−−−+ +−−−−−−−−−−−−+ | 1 | | (ref #456) |−−−−>| 1 | +−−−−−−−−−−−−+ | 1 | +−−−+
如您所见,现在变量引用了不同的数组。
然后你的函数返回并且array
变量消失,最终你创建的数组被垃圾收集。
在你的第二个片段中,你从同样的事情开始,当你进入withSplice
函数之前对splice
的调用时,你有同样的事情你之前做过:
+−−−−−−−−−−−−+ | arr | +−−−−−−−−−−−−+ | (ref #123) |−−+ +−−−−−−−−−−−−+ | +−−−+ | | 1 | +−>| 2 | +−−−−−−−−−−−−+ | | 3 | | array | | +−−−+ +−−−−−−−−−−−−+ | | (ref #123) |−−+ +−−−−−−−−−−−−+
然后你执行你的splice
,改变数组的状态,你就拥有了这个:
+−−−−−−−−−−−−+ | arr | +−−−−−−−−−−−−+ | (ref #123) |−−+ +−−−−−−−−−−−−+ | | +−−−+ +−>| 1 | +−−−−−−−−−−−−+ | | 3 | | array | | +−−−+ +−−−−−−−−−−−−+ | | (ref #123) |−−+ +−−−−−−−−−−−−+
两个变量所指向的数组的状态已被splice
更改。
这些术语在计算机科学中具有非常特殊的含义:它们指的是将变量传递给函数时会发生什么:按值传递意味着变量的值是通过;该函数不知道所有变量,只知道值。传递引用意味着传递到变量;然后,该函数可以达到并修改变量的值,使得调用代码将看到该变化。因此,如果我有一个带有15
的变量,并将通过引用传递给函数,该函数可以将变量的值更改为20
并具有该更改不仅显示在函数内,还显示在函数返回时调用它的代码中。它是"引用"这个词的完全不同的用法。从我们上面的讨论来看,却是一个丰富的误解来源。 : - )
JavaScript纯粹是按值传递。与C ++或C#不同,它根本没有任何传递参考功能。