关于javascript:为什么在函数中改变对象会改变它们在内存中指向的内容,但有时会创建一个新的内存对象?

Why do objects changed in function change what they point to in memory, but sometimes create a new memory object?

本问题已经有最佳答案,请猛点这里访问。

我在这篇博客上读到了如果一个对象或数组在函数内部发生了变化,那么指向内存中的值也会发生变化,就像它在函数外部发生了变化一样。

1
2
3
4
5
var a = [1,2,3],
    b = a;

b[0] = 5;
console.log(a); // [5, 2, 3]

结果与

1
2
3
4
5
6
7
8
9
10
var a = [1,2,3],
    b = a;

var arrayFunction = function(arr){
   arr[0] = 10;
};

var arr = arrayFunction(b);

console.log(a, b, arr) // [10, 2, 3], [10, 2, 3], [10, 2, 3];

然而,我无法理解的是,为什么在函数内重新分配多个数组值不会改变函数外部的值:

1
2
3
4
5
6
7
8
9
10
11
var a = [1,2,3],
    b = a;

var arrayFunction = function(arr){
   arr = ["a","b","c"];
   return arr;
};

var result = arrayFunction(b);
console.log(result) //["a","b","c"]
console.log(a, b);  //[1, 2, 3], [1, 2, 3]

为什么这不会像第一个例子那样改变内存中的指向值?

在JSbin上编写示例可能更好


这是因为JavaScript中的对象实际上不是通过引用传递的。我见过它被称为"通过复制引用传递",或者"通过句柄传递"——这两种方法都能更好地描述实际发生的事情。

在本例中:

1
2
3
4
5
var b = [1, 2, 3];
var arrayFunction = function(arr) {
   arr = ["a","b","c"];
};
arrayFunction(b);

对象引用本身按值传递。您实际上并没有更改b变量的值。但是,您的函数参数(arrb变量最初指向同一对象—因此,如果您更改了一个对象,您可以通过其中一个对象引用该对象,并看到更改。

但是,当您重新指派arr指向另一个对象时,b变量仍然指向旧对象,并且不会更改。


记住,在函数中,arr不是实际数组;arr是指向数组的引用。

所以当你说arr[0]时,你是在检索数组,然后获取或更新其中的一个项。

但是当你说arr = ["a","b","c"]时,你正在创建一个新的对象(在右边),然后把arr变成一个指向新对象的引用。


在javascript中,一个变量只指向一个数组;因此,如果复制该变量,将得到指向同一数组的两个变量:

1
2
var a = [1,2,3];
var b = a;

a[0] = 99改变数组元素,这两个变量都可以观察到,因为只有一个数组,ab都指向它。

当你写的时候

1
a = [5, 6, 7];

您正在设置a以指向另一个不同的数组。

除非您明确要求(例如,使用b = a.slice()),否则javascript不会复制数组。

实际上,任何值(例如对象)都会发生同样的情况。对于数字和字符串,同样的逻辑也是有效的,但是很难注意到副本和共享同一对象之间的区别,因为数字和字符串不能更改(不可变)。

在其他语言(如C和C++)中,变量包含值,而不是指针;因此,在进行赋值时,例如,对象从一个变量复制到另一个变量,如果需要一个指针,则必须明确地请求它。