JavaScript将对象推入数组会更改整个数组

13

我正在使用一种特定的游戏制作框架,但我认为这个问题也适用于JavaScript。

我试图编写一个叙述脚本,让玩家在屏幕底部看到“兽人击中了你”。我想一次显示最后4条消息,并可能允许玩家查看30-50条日志中的消息。为此,我设置了一个对象和一个数组来将对象推入其中。

因此,一开始我像这样设置了一些变量...

servermessage: {"color1":"yellow", "color2":"white", "message1":"", "message2":""},
servermessagelist: new Array(),

当我使用下面的命令(多次)并以不同的数据通过操作servermessage.color1 ... .message1等触发事件时...

servermessagelist.push(servermessage)

它用该数据的副本覆盖整个数组... 有什么想法或者我能做些什么来解决这个问题。

所以,如果我推入color1"RED"和message1"Rover",那么数据是正确的,然后如果我推入 color1"yellow"和message1"Bus",数据就会变成两个副本.color1:"yellow" .message1:"Bus"


你说的“用该数据的副本覆盖整个数组”是什么意思?你能展示一下你用来迭代结果数组的代码吗? - Alexei Levenkov
可能是Push is overwriting previous data in array的重复问题。 - Heretic Monkey
8个回答

28
当您将servermessage推入servermessagelist时,您实际上(或多或少)是推送对该对象的引用。因此,对servermessage所做的任何更改都会反映在您引用它的所有位置。听起来您想要做的是将对象的副本推入列表中。

声明一个如下所示的函数:

function cloneMessage(servermessage) {
    var clone ={};
    for( var key in servermessage ){
        if(servermessage.hasOwnProperty(key)) //ensure not adding inherited props
            clone[key]=servermessage[key];
    }
    return clone;
}

然后每次您想将消息推入列表中时,请执行以下操作:

servermessagelist.push( cloneMessage(servermessage) );

有没有更简单的方法可以通过声明对象数组来完成这个任务?我不知道怎么做。 - Shawn
每次推送“servermessage”之前,您都需要进行克隆。一旦更改它,请克隆它,然后推送其克隆。 - nbrooks
你可以每次声明一个新对象,使用第一个对象的属性并将值设置为null或''。 - nbrooks
@user1481372,修改后的版本可能更容易实现。cloneMessage可以直接使用。在您执行推送操作的所有位置都将您的推送语句替换为我提供的语句。这只是为您创建消息克隆。 - nbrooks

4

在将对象推入数组之前,有两种方法可以使用深拷贝。 1. 使用对象方法创建新对象,然后将其推入数组。

servermessagelist = []; 
servermessagelist.push(Object.assign({}, servermessage));
  1. 使用JSON.stringify方法创建对象的新引用,并使用parse方法将其推入。

    servermessagelist = []; servermessagelist.push(JSON.parse(JSON.stringify(servermessage)));

该方法对于嵌套对象非常有用。


3
当你将对象添加到数组中时,只是添加了一个对该对象的引用。通过将其添加到数组中并没有复制该对象。所以,当你稍后更改该对象并再次将其添加到数组中时,你只是拥有一个带有多个指向同一对象的引用的数组。
为每个添加到数组中创建一个新的对象:
servermessage = {"color1":"yellow", "color2":"white", "message1":"", "message2":""};
servermessagelist.push(servermessage);
servermessage = {"color1":"green", "color2":"red", "message1":"", "message2":"nice work"};
servermessagelist.push(servermessage);

2
我也遇到了同样的问题。我有一个比较复杂的对象,我将其转换为JSON字符串并使用JSON.stringify()将其推入数组中。当从数组返回时,我只需使用JSON.parse()将该字符串转换为JSON对象即可。虽然这是一个相对繁琐的解决方案,但对我来说效果很好。如果你们有其他选择,请在此发帖。

2

我不知道为什么还没有提出使用JSON的方法来做这件事。 您可以先将对象字符串化,然后再解析它以获得对象的副本。

let uniqueArr = [];
let referencesArr = [];
let obj = {a: 1, b:2};

uniqueArr.push(JSON.parse(JSON.stringify(obj)));
referencesArr.push(obj);

obj.a = 3;
obj.c = 5;
uniqueArr.push(JSON.parse(JSON.stringify(obj)));
referencesArr.push(obj);

//You can see the differences in the console logs
console.log(uniqueArr);
console.log(referencesArr);


2
这个解决方案也适用于包含嵌套键的对象。
在推送之前,通过字符串化 obj 来处理。
JSON.stringify(obj)

当你在使用时,请解析。
JSON.parse(obj);

1
这是我唯一有效的方法。在多维循环中,我需要将数组推入另一个数组中。我想利用增量推送状态,但发现所有数组都在数组内部。就像循环结束时的样子。我知道词法作用域和闭包,假设它与外部作用域问题有关。我尝试以多种方式克隆循环内的数组,但发现使用JSON.stringify是唯一有效的方法。我认为JSON.stringify基本上是克隆数组,但不确定为什么.splice和.concat对我不起作用。 - BBi7

1

servermessagelist:new Array() 每次执行时都会清空数组。只需在最初初始化数组时执行该代码一次。


消息列表在我的初始函数中,推送操作在更新函数中,该函数在 WebSocket 事件上运行... - Shawn
啊,我的错,我没有从你的代码示例中得到那个。但正如nbrooks所说,在JavaScript中,对象总是作为引用传递,这就是为什么它不能正确工作的原因。 - riku

0
如上多次提到,最简单的方法是将其转换为字符串,然后将其转换回JSON对象。
this.<JSONObjectArray>.push(JSON.parse(JSON.stringify(<JSONObject>)));

非常好用。


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