提问者:小点点

为什么数组即使在使用Slice之后仍然是链接的


我有一个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;
}

这种行为真的很奇怪,而且它从未检测到映射被更改,更奇怪的是,当我将startend公开到控制台时,start的值取决于我将它记录到的位置。 如果它是在创建之后(在用户做东西之前)记录的,那么它的值与我在end(在用户做东西之后)同时记录它的值是不同的。 每当我在用户完成操作后记录它时,它的值总是与map相同,这意味着在检查是否有任何变化的过程中,startend总是相同的。

我唯一能想到的是,这是由于链接,但是在其中添加.slice(0)并没有改变什么


共2个答案

匿名用户

看起来map是数组的数组,实际数据在内部数组中。

因此,letstart=this.map.slice(0)所做的只是将对这些内部数组的引用的数组复制到一个数组start中。 这些引用现在在mapstart中是相同的--它只是一堆(引用)值。

当您更新map中的值时,这些引用在mapstart中指向相同的更新后的内部数组,因为这些引用是相同的。

因此,对于任何索引,即使map!===start(试试看),map[0]===start[0]以及因此map[0][0]====start[0]也是如此。 它们是不同的数组,它们不是链接的,但是它们包含的数据是--即,它是相同的数据。

匿名用户

Slice创建一个浅层副本,这意味着新创建的数组仍然引用原始数组。

检查:Javascript切片方法是否返回浅层副本?