我有一个js游戏编辑器,为了使它具有撤消功能,我需要存储编辑器地图的最后几个状态。 我需要它只推以前的地图到堆栈,如果他们是不同的堆栈上的第一个地图,这意味着地图将被推,只有当他们不是相同的最后一个。 这样可以防止复制映射在每帧都被推入,从而占用堆栈中的所有空间,并且不得不撤消数十次才能回到人类对上一个状态的感知。
一开始,我以为这会很容易,只需保存映射的start
条件,做stuff,然后保存end
条件。 如果它们不同,则说明用户更改了某些内容,并且开始条件应该被推送到堆栈上。 若要撤消,只需将当前映射设置为堆栈上的最后一个内容。
问题是这些数组中的一些似乎是链接的,并且正在触发我的函数来检查它们是否相等。
//This code is part of the method that handles the mouse,
//inside the editor class:
let start = this.map.slice(0);
//DO STUFF
let end = this.map.slice(0);
if(!ArrayIsEqual2d(start, end)){
console.log('changed')
this.lastMaps.unshift(start);
if(this.lastMaps.length > this.undoBuffer){
this.lastMaps.pop();
}
}
//End code inside of editor class
function ArrayIsEqual2d(one, two){
if(one == null || two == null || one == undefined || two == undefined){
return false;
}
if(one.length !== two.length){
return false;
}
for(let i = 0; i < one.length; i++){
if(one[i].length !== two[i].length){
return false;
}
for(let j = 0; j < one[i].length; j++){
if(one[i][j] !== two[i][j]){
return false;
}
}
}
return true;
}
这种行为真的很奇怪,而且它从未检测到映射被更改,更奇怪的是,当我将start
和end
公开到控制台时,start的值取决于我将它记录到的位置。 如果它是在创建之后(在用户做东西之前)记录的,那么它的值与我在end(在用户做东西之后)同时记录它的值是不同的。 每当我在用户完成操作后记录它时,它的值总是与map
相同,这意味着在检查是否有任何变化的过程中,start
和end
总是相同的。
我唯一能想到的是,这是由于链接,但是在其中添加.slice(0)
并没有改变什么
看起来map
是数组的数组,实际数据在内部数组中。
因此,letstart=this.map.slice(0)
所做的只是将对这些内部数组的引用的数组复制到一个数组start
中。 这些引用现在在map
和start
中是相同的--它只是一堆(引用)值。
当您更新map中的值时,这些引用在map
和start
中指向相同的更新后的内部数组,因为这些引用是相同的。
因此,对于任何索引,即使map!===start
(试试看),map[0]===start[0]
以及因此map[0][0]====start[0]
也是如此。 它们是不同的数组,它们不是链接的,但是它们包含的数据是--即,它是相同的数据。
Slice创建一个浅层副本,这意味着新创建的数组仍然引用原始数组。
检查:Javascript切片方法是否返回浅层副本?