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

事件函数中修改全局变量:井字棋游戏代码修正求助

解决井字棋交替落子的问题

嘿,我来帮你搞定这个井字棋的问题!你的代码里主要有几个逻辑偏差,尤其是事件绑定的方式和gameState的判断时机,还有var带来的作用域问题,咱们一步步理清楚:

问题分析

  1. 重复绑定事件:你在循环里调用chasviX(i)的时候,直接执行了这个函数,而不是把它作为回调传入addEventListener,而且chasviX内部又绑定了一次点击事件,导致每个格子被绑定了两次点击事件,逻辑彻底混乱。
  2. gameState判断时机错误:循环执行的时候gameState是0,所以所有格子都绑定了X的逻辑,之后gameState变化也不会重新触发绑定,自然所有点击都是X。
  3. var的作用域问题:用var声明的i是函数级作用域,循环结束后所有回调共享同一个i的值,不过这在你的代码里不是最核心的问题,但也是需要修正的点。

修正后的完整代码

HTML(和你的原代码一致,无需修改)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tic Tac Toe</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="main">
        <table>
            <tr>
                <td class="b1-1"></td>
                <td class="b1-2"></td>
                <td class="b1-3"></td>
            </tr>
            <tr>
                <td class="b2-1"></td>
                <td class="b2-2"></td>
                <td class="b2-3"></td>
            </tr>
            <tr>
                <td class="b3-1"></td>
                <td class="b3-2"></td>
                <td class="b3-3"></td>
            </tr>
        </table>
    </div>
    <script src="script.js" type="text/javascript"></script>
</body>
</html>

CSS(和你的原代码一致,无需修改)

.main {
    padding: 100px 0;
    width: 360px;
    margin: 0 auto;
}
table, tbody {
    margin: 0;
    padding: 0;
    width: 360px;
    height: 360px;
}
tr {
    width:360px;
    height: 120px;
    margin: 0;
    padding: 0;
}
td {
    text-align: center;
    width:120px;
    height: 120px;
    border: 1px solid #333;
    margin: 0;
    padding: 0;
    font-size: 50px;
}

JavaScript(核心修正部分)

// 用const/let代替var,避免作用域问题
const cells = document.querySelectorAll('td');
let gameState = 0; // 0代表X回合,1代表O回合

// 给每个格子绑定一次点击事件
cells.forEach(cell => {
    cell.addEventListener('click', () => {
        // 先判断格子是否已经被填充,防止重复点击
        if (cell.textContent !== '') return;
        
        // 根据gameState设置内容,完成后切换回合
        if (gameState === 0) {
            cell.textContent = 'X';
            gameState = 1;
        } else {
            cell.textContent = 'O';
            gameState = 0;
        }
    });
});

关键改进点解释

  1. 简化事件绑定:用forEach遍历所有格子,给每个格子只绑定一次点击事件,彻底避免重复绑定的问题。
  2. 实时判断gameState:把gameState的判断放在点击事件的处理函数里,每次点击时才检查当前该落哪个子,这样就能实现交替落子。
  3. 防止重复点击:增加了if (cell.textContent !== '') return;的判断,已经有内容的格子不会再被点击修改。
  4. 作用域优化:用const和let代替var,forEach里的cell是块级作用域,不会出现原代码里共享变量的问题。

关于闭包和作用域链的补充

你提到对闭包和作用域链理解有偏差,原代码里用var声明i的时候,循环里的所有回调函数共享同一个i的引用,循环结束后i的值是9(因为cells有9个元素),所以如果原代码的事件绑定逻辑正确的话,所有点击都会操作最后一个格子。而用let或者forEach的话,每个迭代的变量都是独立的块级作用域,就不会出现这个问题啦。

内容的提问来源于stack exchange,提问作者Levan Sakvarelidze

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 17:07:49