在“for”循环中填充多维数组(JavaScript)

3

看起来JavaScript试图优化代码,因此如果我们想要在循环中使用以下代码用一维数组(smallArr)的可变值填充多维数组(largeArr):

largeArr = []
smallArr = []

for (i=0; i<2; i++)
{
    smallArr[0]=i
    smallArr[1]=2*i

    largeArr[i]=smallArr
}

我们得到了一个意外的结果:largeArr=[[1,2],[1,2]] (应该是 [[0,0],[1,2]])。因此,Javascript 首先计算 smallArr 的值,然后才填充 largeArr。 为了得到正确的结果,我们必须在循环中声明 smallArr
largeArr = []

for (i=0; i<2; i++)
{
    smallArr = []
    smallArr[0]=i
    smallArr[1]=2*i

    largeArr[i]=smallArr
}

然后它按预期工作(largeArr=[[0,0],[1,2]])。

为什么会出现这种行为?


在第一种情况下,相同的数组引用被插入到largeArr中,因此第二次循环中的修改将只修改单个实际数组的内容,其引用在largeArr中被插入两次。 - nhahtdh
在测试像这样的代码时,使用开发人员工具在您的JavaScript中插入断点可能会有所帮助。这样,您就可以看到结果不是由于优化而产生的,尽管如果您不知道指针是什么,我想它仍然会相当令人费解。 - Jeff
4个回答

4
因为指针,这就是为什么。JavaScript在这方面(仅限于这个方面)效仿Java和C语言。当你进行赋值操作时,会发生这种情况。
largeArr[i] = smallArr

你正在分配一个指针。指针的解释如下:

在C语言中(在Java和Javascript中也有类似情况),没有基本数组类型,而是数组指向内存中的一块空间,您可以将该空间填充为任何您想要的信息(或者更确切地说,您已经声明了)。指针在内存中的存在方式是什么?它是一个四字节(或八字节、两字节,具体取决于您的系统)的内存地址,告诉编译器/解析器从哪里获取相应的信息。因此,当您进行分配时,您告诉它:“嘿,将largeArr[i]设置为smallArr的内存地址。”因此,当您对smallArr进行更改时,每次解除引用数组时都会反映出来,因为它实际上是同一个数组。但当您执行以下操作时:

smallArr = [] 

在循环内部,你要说:“创建一个新的数组,并将smallArr设置为该数组的地址。”这样,数组就会保持分离。

好的。对于楼主,作为一个实验,请将 largeArr[0][0] 赋值为 3,然后查看 largeArr 的内容。 - Jeff B

0

在最后的for循环中

smallArr[0]=i
smallArr[1]=2*i  

(其中i=1)上述代码被转换为:

smallArr[0]=1
smallArr[1]=2

而你的大数组就是这个:

[smallArr, smallArr]

这导致了 意外的 结果:

[[1, 2], [1, 2]]

在JavaScript中,对象是通过引用复制的(一种C风格指针)。为了获得所需的结果,必须按值复制数组,或在每个循环中分配不同的数组:
var largeArr = [];

for (i=0; i<2; i++)
   largeArr[i] = [[i, 2*i]];

0

使用代码行largeArr[i]=smallArr,您将i属性设置为指向smallArr的引用。您不会复制它。最终,largeArr的所有属性都将指向同一个smallArr,其中每次都覆盖了值。

通过在每个循环中初始化smallArr,您创建了新对象;因此,largeArr的每个属性都将指向不同的数组。顺便说一下,这是一个赋值,而不是声明 - 您应该将变量声明为本地变量(函数内)并使用var语句


0

当您像上面那样分配数组引用时,您并没有分配该数组的,而只是一个指向该数组的引用

将其视为指针。largeArr [0]和largeArr [1]指向smallArr,并且循环迭代仅更改smallArr的内容。largeArr所指向的对象并没有改变。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接