俄罗斯方块旋转函数修改原始形状对象问题求助
嘿,我一眼就看出问题出在哪了——你遇到的是浅拷贝导致的引用共享问题!
为什么旋转会修改原始pieces对象?
你在choosePiece里用了slice(0)来拷贝方块数组,还有rotate函数里也用了grid.slice(0),但这俩都是浅拷贝。对于二维数组来说,slice(0)只会复制外层数组的结构,内层的每个子数组还是和原始pieces里的子数组指向同一个内存地址。
举个例子:当你拿到pieces["I"]并执行.slice(0),你得到的是一个新的外层数组,但里面的每一行[0,1,0,0]还是原始数组的引用。所以当rotate函数里交换piece[i][j]和piece[j][i]的时候,你其实是在修改原始pieces里的内层数组元素!
另外,你用了Object.freeze(pieces),但这个方法是浅冻结,它只会冻结外层对象的属性,内层的数组依然可以被修改,所以原始方块的形状还是会被改动。
解决办法:用深拷贝彻底切断引用
要解决这个问题,我们需要对二维数组进行深拷贝,确保每个层级的数组都是新的副本,和原始数据完全无关。
1. 修改choosePiece函数
把浅拷贝换成深拷贝,对每个子数组也进行拷贝:
function choosePiece() { const choices = "OISZLJT"; const chosenLetter = choices[~~(Math.random() * choices.length)]; // 深拷贝二维数组:遍历每一行并拷贝 const chosenPiece = pieces[chosenLetter].map(row => row.slice()); return chosenPiece; }
2. 修改rotate函数
同样,在旋转前先深拷贝输入的方块数组,避免修改传入的原数组:
function rotate(grid, num) { // 深拷贝输入的grid,确保操作的是副本 const piece = grid.map(row => row.slice()); const size = piece.length; // 矩阵转置逻辑不变 for (let i = 0; i < size; i++) { for (let j = i; j < size; j++) { [piece[i][j], piece[j][i]] = [piece[j][i], piece[i][j]]; } } // 根据旋转方向反转数组 return num > 0 ? piece.map(row => row.reverse()) : piece.reverse(); }
为什么这样有效?
map(row => row.slice())会创建一个全新的外层数组,并且每个内层数组都是原行的拷贝,这样你后续对这个副本的任何修改(比如旋转)都不会影响原始的pieces对象。下一次生成同类型方块时,拿到的依然是最初定义的原始形状,不会被之前的旋转操作改变。
额外小提示
如果你想彻底保护pieces里的原始数据,可以考虑用深冻结,但一般来说只要确保每次使用的都是深拷贝后的副本,就足够解决问题了。
内容的提问来源于stack exchange,提问作者Michael Z

