任何人都可以在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'方法似乎接收到了实际的数组.是什么赋予了?提前致谢!
为什么数组通过值传递一次而不是其他?
它没有.在两种情况下,对数组的引用(按值)传递给函数.在第一个代码段中,然后使用对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功能.