如何在井字棋游戏中正确执行removeEventListener?
井字棋游戏:移除按钮点击事件的正确实现及代码优化
问题背景
我是编程新手,写了一款简易井字棋游戏的基础代码,规则是任意一方获胜次数达到5次时游戏结束。我尝试移除按钮的点击事件,但写法没生效。
原始代码:
function game(id) { let round = ''; round = playRound(id, getComputerChoice()); if(round == "win") { win++; } else if(round == "lose") { lose++; } score.textContent = "Score is " + win + "-" + lose; if(win == 5) { over.textContent = "Game is over, you win!"; } else if(lose == 5) { over.textContent = "Game is over, you lose!"; } } let win = 0; let lose = 0; const result = document.querySelector('#results'); const buttons = document.querySelectorAll('button'); const score = document.querySelector('#score'); const over = document.querySelector('#over'); buttons.forEach((button) => { button.addEventListener('click', () => game(button.id)); });
错误尝试(无法移除事件):
function game(id) { ... if(win == 5) { over.textContent = "Game is over, you win!"; button.removeEventListener('click', () => game(button.id)); } else if(lose == 5) { over.textContent = "Game is over, you lose!"; button.removeEventListener('click', () => game(button.id)); } }
错误原因
- 匿名函数引用不匹配:
removeEventListener要求传入的函数必须和addEventListener时的是同一个引用。你每次写() => game(button.id)都是创建新的匿名函数,所以无法匹配移除。 - 变量未定义:
game函数里的button变量没有定义,找不到对应的DOM元素。
正确实现方式
方式一:使用命名事件处理函数
把点击事件的处理逻辑抽成命名函数,确保添加和移除时用同一个函数引用:
// 定义命名的点击处理函数 function handleButtonClick(e) { game(e.target.id); } function game(id) { const round = playRound(id, getComputerChoice()); if(round === "win") { win++; } else if(round === "lose") { lose++; } score.textContent = `Score is ${win}-${lose}`; if(win === 5) { over.textContent = "Game is over, you win!"; // 遍历所有按钮移除点击事件 buttons.forEach(button => button.removeEventListener('click', handleButtonClick)); } else if(lose === 5) { over.textContent = "Game is over, you lose!"; buttons.forEach(button => button.removeEventListener('click', handleButtonClick)); } } let win = 0; let lose = 0; const result = document.querySelector('#results'); const buttons = document.querySelectorAll('button'); const score = document.querySelector('#score'); const over = document.querySelector('#over'); // 添加事件时使用命名函数 buttons.forEach(button => button.addEventListener('click', handleButtonClick));
方式二:添加游戏状态判断(更简单)
不用移除事件,而是在每次点击时先判断游戏是否已经结束,结束则直接返回:
let win = 0; let lose = 0; let isGameOver = false; // 新增游戏状态标记 const result = document.querySelector('#results'); const buttons = document.querySelectorAll('button'); const score = document.querySelector('#score'); const over = document.querySelector('#over'); function game(id) { // 游戏已结束则直接返回,不执行后续逻辑 if(isGameOver) return; const round = playRound(id, getComputerChoice()); if(round === "win") { win++; } else if(round === "lose") { lose++; } score.textContent = `Score is ${win}-${lose}`; if(win === 5) { over.textContent = "Game is over, you win!"; isGameOver = true; } else if(lose === 5) { over.textContent = "Game is over, you lose!"; isGameOver = true; } } buttons.forEach((button) => { button.addEventListener('click', () => game(button.id)); });
代码优化建议
- 使用模板字符串简化拼接:把
"Score is " + win + "-" + lose改成Score is ${win}-${lose},更易读简洁。 - 提取常量:把获胜次数设为常量,方便后续修改:
const WINNING_SCORE = 5;,判断时用win === WINNING_SCORE。 - 封装游戏结束逻辑:避免重复代码,把结束游戏的操作抽成单独函数:
function endGame(message) { over.textContent = message; isGameOver = true; // 或者用方式一的移除事件逻辑 } // 在game函数里调用: if(win === WINNING_SCORE) { endGame("Game is over, you win!"); } else if(lose === WINNING_SCORE) { endGame("Game is over, you lose!"); }
- 统一使用严格相等运算符:用
===替代==,避免隐式类型转换带来的意外问题。 - 减少变量声明冗余:
let round = ''; round = playRound(...)可以简化为const round = playRound(...),因为round的值不会再修改。
内容的提问来源于stack exchange,提问作者Henry Zhang
相关产品推荐
相关产品推荐

