为什么数组通过值传递一次而不是其他?

时间:2015-03-03 07:33:00

标签: javascript arrays

任何人都可以向我解释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'方法似乎收到了实际的数组。是什么赋予了?提前谢谢!

1 个答案:

答案 0 :(得分:6)

  

为什么数组有时会被值传递而不是其他数据?

它没有。在两种情况下,数组的引用都会(按值)传递给函数。在您的第一个代码段中,然后替换 array变量中的引用,并引用新数组; array变量与您的arr变量无关,后者一直引用旧数组。在第二个代码段中,您修改了arrayarr所指的原始数组的状态

(更多关于术语"按值传递"和"传递参考",具有非常具体的含义,如下所示。)

让我们退一步,然后我们会看看你的片段。

在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变量的值。由于arrayarr都引用同一个数组,因此无论您使用哪个引用副本查看它,都会看到修改后的状态。

更多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#不同,它根本没有任何传递参考功能。