JS开发战列舰游戏棋盘:放置船只时矩阵为何会同时更新两列
问题复现
你在使用JS开发战列舰游戏的棋盘功能时,遇到了船只放置测试不通过的问题,相关代码如下:
Gameboard类代码
export default class Gameboard { constructor(size) { this.grid = this.#createMatrix(size); } #createMatrix(size) { const mat = new Array(size); for (let i = 0; i < size; i++) { // ship - 存储船只对象(如果有),attacked - 该格子是否被攻击 // index - 船只当前格子相对于整艘船的位置索引(如果有) mat[i] = new Array(size).fill({ ship: null, attacked: false, index: -1 }); } return mat; } /** * @param {Ship} ship 要放置的船只对象 * @param {int} x 船只放置的起始x坐标 * @param {int} y 船只放置的起始y坐标 */ placeShip(ship, x, y) { for (let i = 0; i < ship.length; i++) { this.grid[x][y]["ship"] = ship; this.grid[x][y]["index"] = i; console.log(this.grid[x][y]); y++; // 水平放置,每次移动到下一列 } } }
测试用例代码
it("places ships", () => { const gameboard = new Gameboard(2); const ship = new Ship(2); gameboard.placeShip(ship, 0, 0); expect(gameboard.grid).toEqual([ [ { ship: ship, index: 0, attacked: false }, { ship: ship, index: 1, attacked: false }, ], [defaultObj, defaultObj], ]); });
测试运行后不符合预期:循环中修改单个格子时,同一行的两列对象同时被更新,最终两个格子的index都为1,导致用例失败。
问题原因
问题出在#createMatrix方法中Array.fill()的使用上:
- 当
fill()方法的参数是**引用类型(比如对象、数组)**时,JS不会为每个数组元素创建新的对象,而是把同一个对象的引用复制到数组的所有位置。 - 你代码里
new Array(size).fill({ ship: null, attacked: false, index: -1 })执行后,同一行的所有格子都指向同一个对象,修改任意一个格子的属性,整行所有格子的属性都会同步变化。 - 所以你第一次修改
(0,0)的index为0时,(0,1)的index也变成了0;第二次修改(0,1)的index为1时,(0,0)的index又被覆盖成了1,最终两个格子的index都是1,和预期不符。
修复方案
把填充数组的逻辑改为给每个位置单独创建新对象即可,修改#createMatrix中的对应代码:
// 原错误代码 // mat[i] = new Array(size).fill({ ship: null, attacked: false, index: -1 }); // 修复后代码 mat[i] = new Array(size).fill(null).map(() => ({ ship: null, attacked: false, index: -1 }));
这里先用fill(null)占位数组,再通过map为每个位置返回独立的新对象,修改单个格子不会影响其他格子。
内容的提问来源于stack exchange,提问作者user16037629
相关产品推荐
相关产品推荐

