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

如何在井字棋游戏中正确执行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));
    }
}

错误原因

  1. 匿名函数引用不匹配:removeEventListener要求传入的函数必须和addEventListener时的是同一个引用。你每次写() => game(button.id)都是创建新的匿名函数,所以无法匹配移除。
  2. 变量未定义: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));
});

代码优化建议

  1. 使用模板字符串简化拼接:把"Score is " + win + "-" + lose改成Score is ${win}-${lose},更易读简洁。
  2. 提取常量:把获胜次数设为常量,方便后续修改:const WINNING_SCORE = 5;,判断时用win === WINNING_SCORE。
  3. 封装游戏结束逻辑:避免重复代码,把结束游戏的操作抽成单独函数:
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!");
}
  1. 统一使用严格相等运算符:用===替代==,避免隐式类型转换带来的意外问题。
  2. 减少变量声明冗余:let round = ''; round = playRound(...)可以简化为const round = playRound(...),因为round的值不会再修改。

内容的提问来源于stack exchange,提问作者Henry Zhang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 17:50:33