事件函数中修改全局变量:井字棋游戏代码修正求助
解决井字棋交替落子的问题
嘿,我来帮你搞定这个井字棋的问题!你的代码里主要有几个逻辑偏差,尤其是事件绑定的方式和gameState的判断时机,还有var带来的作用域问题,咱们一步步理清楚:
问题分析
- 重复绑定事件:你在循环里调用
chasviX(i)的时候,直接执行了这个函数,而不是把它作为回调传入addEventListener,而且chasviX内部又绑定了一次点击事件,导致每个格子被绑定了两次点击事件,逻辑彻底混乱。 - gameState判断时机错误:循环执行的时候
gameState是0,所以所有格子都绑定了X的逻辑,之后gameState变化也不会重新触发绑定,自然所有点击都是X。 - 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; } }); });
关键改进点解释
- 简化事件绑定:用
forEach遍历所有格子,给每个格子只绑定一次点击事件,彻底避免重复绑定的问题。 - 实时判断gameState:把
gameState的判断放在点击事件的处理函数里,每次点击时才检查当前该落哪个子,这样就能实现交替落子。 - 防止重复点击:增加了
if (cell.textContent !== '') return;的判断,已经有内容的格子不会再被点击修改。 - 作用域优化:用
const和let代替var,forEach里的cell是块级作用域,不会出现原代码里共享变量的问题。
关于闭包和作用域链的补充
你提到对闭包和作用域链理解有偏差,原代码里用var声明i的时候,循环里的所有回调函数共享同一个i的引用,循环结束后i的值是9(因为cells有9个元素),所以如果原代码的事件绑定逻辑正确的话,所有点击都会操作最后一个格子。而用let或者forEach的话,每个迭代的变量都是独立的块级作用域,就不会出现这个问题啦。
内容的提问来源于stack exchange,提问作者Levan Sakvarelidze
相关产品推荐
相关产品推荐

