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

sun*_*tal 2 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]
Run Code Online (Sandbox Code Playgroud)

我的思维过程在哪里失败?我觉得'重新分配'函数正在接收数组的副本,因此重新分配对原始函数没有任何作用,但是'withSplice'方法似乎接收到了实际的数组.是什么赋予了?提前致谢!

T.J*_*der 6

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

它没有.在两种情况下,对数组的引用(按值)传递给函数.在第一个代码段中,然后使用对array新数组的引用替换变量中的引用; 该array变量与您的arr变量无关,该变量始终引用旧数组.在第二个代码段中,您将修改两个和所引用的原始数组的状态.arrayarr

(更多关于"传值"和"传递参考"的术语,下面有非常具体的含义.)

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

在JavaScript中(与许多语言一样),变量只包含原始值,如(例如)数字15.一种原始值是对数组或其他类型对象的引用,它存在于变量的内存中.这些引用是我们从未直接看到的值,告诉JavaScript引擎在内存中的其他地方找到数组/对象的位置.

所以说我们有这个代码:

var n = 15;
var a = [1, 2, 3];
Run Code Online (Sandbox Code Playgroud)

这是我们在内存中得到的一些ASCII艺术:

+?????+
|  n  |
+?????+
| 15  |
+?????+

+??????????????+
|      a       |     +???+
+??????????????+     | 1 |
| (ref #123)   |????>| 2 |
+??????????????+     | 3 |
                     +???+

n包含值15; a包含指向数组的对象引用,在内存中的其他位置.

将变量的值传递给函数(或将其分配给另一个变量等)时,会生成其中的原始值的副本.在对象引用的情况下复制的值是对象引用; 然后,引用的两个副本都引用同一个对象.所以,如果我们这样做:

var m = n;
var b = a;
Run Code Online (Sandbox Code Playgroud)

我们得到:

+?????+
|  n  |
+?????+
| 15  |
+?????+

+?????+
|  m  |
+?????+
| 15  |
+?????+

+??????????????+
|      a       |
+??????????????+
| (ref #123)   |??+
+??????????????+  |  +???+
                  |  | 1 |
                  +?>| 2 |
+??????????????+  |  | 3 |
|      b       |  |  +???+
+??????????????+  |
| (ref #123)   |??+
+??????????????+

在这两种情况下,变量的值都被复制到一个新变量中.

那么你的片段中发生了什么呢?

在第一个片段中,您将为array变量(参数)提供一个新值,即对新数组的引用,与原始数组分开.由于array变量与变量没有连接arr,因此arr仍然引用原始数组.

在第二个片段中,您将更改原始数组的状态,而不是array变量的值.由于两个array和arr指的是同一个阵列,可以看到修改后的状态,无论你用它来看看它其中引用的副本.

更多ASCII艺术:

你的第一个片段

在您的第一个代码段中,您将从内存开始:

+????????????+
|    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#不同,它根本没有任何pass-by-reference功能.