You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 04:07:07