JavaScript 2048游戏左右滑动时originalBoard保存异常问题
问题分析:2048游戏左右滑动时矩阵保存异常
我是JavaScript初学者,正在开发2048游戏。需求是每次按下方向键修改矩阵前,将当前矩阵保存到originalBoard变量,用于后续与修改后的矩阵对比。
上下滑动函数(如slideUp)可正常保存修改前的矩阵,但左右滑动函数(如slideLeft)中,尽管在矩阵修改前执行了originalBoard = board,最终originalBoard却与修改后的矩阵完全一致。
此外,在按键监听事件中,按下左右方向键时,先调用console.log(board)再执行滑动函数,打印出的board却是修改后的矩阵;而上下方向键则能正确打印修改前的矩阵。
相关函数代码
slideUp函数
function slideUp() { originalBoard = board board = board[0].map((val, index) => board.map(row => row[row.length-1-index])); for (let r = 0; r < rows; r++) { let row = board[r] row = slide(row) board[r] = row } board = board[0].map((val, index) => board.map(row => row[index]).reverse()) if (notEqual(board, originalBoard)) { addNewNum() } tileUI() }
slideLeft函数
function slideLeft() { originalBoard = board for (let r = 0; r < rows; r++) { let row = board[r] row = slide(row) board[r] = row } if (notEqual(board, originalBoard)) { addNewNum() } tileUI() }
slide函数
function slide(row) { row = filterZero(row) for (let i = 0; i < row.length - 1; i ++) { if (row[i] == row[i + 1]) { row[i] = row[i] + row[i + 1] row[i + 1] = 0 score += row[i] } } row = filterZero(row) while(row.length < 4) { row.push(0) } return row }
按键监听代码
document.addEventListener('keyup', (e) => { if (e.code == 'ArrowLeft') { console.log(board) slideLeft() } if(e.code == 'ArrowRight') { console.log(board) slideRight() } if(e.code == 'ArrowUp') { console.log(board) slideUp() } if(e.code == 'ArrowDown') { console.log(board) slideDown() } })
核心原因:JavaScript引用类型的特性
1. 左右滑动时的引用指向问题
JavaScript里数组属于引用类型,执行originalBoard = board时,并没有复制数组的实际内容,只是让originalBoard和board指向内存里的同一个数组对象:
slideUp函数中,你通过board = board[0].map(...)创建了一个全新的数组对象并赋值给board,此时originalBoard仍然指向原来的数组,后续修改新board不会影响它。slideLeft函数中,你直接修改了原数组的内部元素(通过board[r] = row更新每一行),但originalBoard和board还是指向同一个数组,所以originalBoard会跟着变成修改后的内容。
2. console.log的惰性求值问题
浏览器控制台对引用类型采用惰性求值:只有当你点击展开console.log的数组时,才会去读取它的当前值:
- 上下滑动时,
board被重新赋值为新数组,console.log记录的是旧数组的引用,展开时读取的是旧数组的原始内容。 - 左右滑动时,
board还是原来的数组,console.log记录的引用指向的数组后续被修改了,所以展开时看到的是修改后的结果。如果想打印当前快照,可以用console.log(JSON.parse(JSON.stringify(board)))复制数组内容后再打印。
解决方案:深复制矩阵到originalBoard
要解决这个问题,保存originalBoard时需要复制整个矩阵的内容,而不是只复制引用。可以用嵌套的map实现深复制:
// 替换原来的 originalBoard = board originalBoard = board.map(row => [...row]);
把这个修改应用到slideLeft、slideRight函数中,slideUp和slideDown也可以统一改成这种方式,避免后续出现类似问题。
修改后的slideLeft示例:
function slideLeft() { // 深复制矩阵 originalBoard = board.map(row => [...row]); for (let r = 0; r < rows; r++) { let row = board[r] row = slide(row) board[r] = row } if (notEqual(board, originalBoard)) { addNewNum() } tileUI() }
这样originalBoard就会保存修改前的矩阵快照,和修改后的board是两个独立的数组对象,对比时就能得到正确结果。
内容的提问来源于stack exchange,提问作者blackburn
相关产品推荐
相关产品推荐

