奇怪的Javascript数组同步技巧?

奇怪的Javascript数组同步技巧?,javascript,arrays,html,pointers,Javascript,Arrays,Html,Pointers,我发现了一个奇怪的JS行为: var myArray = [ [1] , [2] , [3] ]; var myArrayCopy = []; myArrayCopy.push( myArray[1] ); alert( myArrayCopy ); // 2, as expected. myArrayCopy[0][0] = 'foo'; alert( myArrayCopy ); // 'foo', as expected. alert( myArray ); // 1, foo

我发现了一个奇怪的JS行为:

var myArray = [ [1] , [2] , [3] ];

var myArrayCopy = [];

myArrayCopy.push( myArray[1] );

alert( myArrayCopy ); // 2, as expected.

myArrayCopy[0][0] = 'foo';

alert( myArrayCopy ); // 'foo', as expected.

alert( myArray ); // 1, foo, 3  =  WTF ? :)

请注意,如果我们直接推送值而不是数组,这将不起作用

对我来说,这看起来像是将数组推入一个数组,以某种方式转换为只将引用推入那些数组,而不是副本(这不是人们所期望的行为,如果我错了,请更正)

有人能解释为什么吗?

别名

您正在输入
myArrayCopy
myArray
中包含的现有对象(数组)的引用。因此,当您修改该数组时,您正在修改上述两个数组中引用的实际对象

请注意,当您在JavaScript中传递对象时,它们不会被复制,而是传递对实例的引用,并在接收方范围内对其进行修改,这也会导致调用方范围内的对象被修改。 这条规则有一些明显的例外(例如原语类型,因为它们不是对象,或者字符串,因为它们实际上是在写入时复制的,等等),但是让我们集中讨论您的问题

考虑到数组是一个对象,在下面的示例中也会发生同样的情况,但从我的观点来看,情况要清楚得多:

var o = { "foo": " bar" };
myArray[0] = o;
myArrayCopy[0] = myArray[0];
o.foo = "nolongerbar";
它返回什么
myArrayCopy[0]。foo
?还有什么
myArray[0]。foo

这在大多数OO语言中被称为别名,无论如何,在处理对象引用时,这是一个常见的错误

编辑


哦,无论如何这都不是同步技巧,这通常是令人讨厌的bug背后的原因。:-)

myArray[1]的值是一个数组(
[2]
)。在
myArrayCopy.push(myArray[1])中将其推入
myArrayCopy
,您推送数组,而不是它的内容(
[2]
,而不是
2


当您稍后对该数组进行变异时(
myArrayCopy[0][0]='foo';
),它显然会影响使用该数组的所有地方。

是的,这是有意义的操作,因为myArray和myArrayCopy[0]具有相同的内存地址。您可以检查下面的调试捕获


在通过第9行之后,它们同时被myArray和myArraycopy的值更改为“foo”

这是完全正确的

由于数组充当对象,因此它存储内存地址 而不是存储值本身

var myArray = [ [1] , [2] , [3] ];
myArray
将存储所创建数组的地址(比如
addr-1

由于数组的元素本身就是一个数组,它现在将有三个地址分别对应于数组
[1]
[2]
[3]
比如
addr-2
addr-3
addr-4

因此,最后我们有:

addr-1 = [addr-2,addr-3,addr-4]

这将存储一个新地址,例如
addr-5=[]

myArrayCopy.push(myArray[1]);
因为您将
myArray
的第一个元素推送到
myArrayCopy
。它将存储地址
addr-3
。像这样
addr-5=[addr-3]

`alert( myArrayCopy ); // 2, as expected
因此,这当然很好,因为
addr-3=[2]

myArrayCopy[0][0] = 'foo';
这里您正在修改
myArrayCopy
的第0个元素的第0个元素的值,即将
addr-3
处的第一个元素更改为
foo

alert( myArrayCopy ); // 'foo', as expected
因此,它也起作用:

alert( myArray ); // 1, foo, 3
myArray
现在有
addr-1=[addr-2,addr-3,addr-4]
,其中
addr-2=[1]

addr-3=[“foo”]
addr-4=[3]

数组是通过ref传递的,这并不是说你在编辑一个ref,而是在编辑一个ref。你可以使用.slice()获得一个具有相同值的新容器(浅拷贝)@dandavis-你的注释在概念上是正确的,但不准确。JavaScript中没有byRef这样的东西,但是传递一个对象(即数组)会产生您所描述的效果。在js中,它与可变/不可变的内容保持一致:数字、字符串、布尔值是不可变的,因此按值传递,而可变的对象(也称为对象)则作为对象引用传递。迂腐地说,从技术上讲,obj不是通过ref传递的,它是一个指向ref的指针,通过值传递,但这只是混淆,其效果是传递“byRef”…@dandavis不,这不是真的。同样,ByRef是VB。(类似于代码>空白f(int v);C++中的,和<代码>空腹F(REF INTV);中的<代码>,但是这导致调用函数时使用的变量(地址)的引用(“传入”),实际上让函数改变了外部变量。JavaScript所做的更像是
void f(int v)
void f(obj*v)
,在第二个函数中使用对象的地址,并且可以从外部看到对象的变化。我猜OP实际上对js如何在引擎盖下工作以及它的相似性不感兴趣。从他的问题来看,他似乎仍在触及语言的表面。:-)我知道推送一个数组而不是它的内容,我的问题是,当我更改值myArrayCopy[0][0]时,它对第一个数组的影响如何“明显”!但我认为其他答案可能已经回答了这个问题:)@lapin-这是相同的数组。这就是我在回答中试图解决的问题:你推送数组,而不是它的内容,当你以后改变这个数组时。它一直都是同一个数组。你的答案看起来像它应该的格式吗?(环顾四周,在人群中很奇怪)我知道不是,我是第一次回答,所以我只是随便玩玩。谢谢你完整的回答。特别是为了指出“别名”这一术语!不客气。这是一个广泛使用的术语,尽管在关于JavaScript的讨论中提到它并不常见。我不知道为什么,也许有一个术语具有相同的含义,但我发现别名是最有意义的(当然,至少对我来说是如此)。
alert( myArray ); // 1, foo, 3