
本文详解 JavaScript 中 setTimeout 的常见误用,以及如何准确筛选未设置图片的 <img> 元素实现井字棋人机交互逻辑。
本文详解 javascript 中 `settimeout` 的常见误用,以及如何准确筛选未设置图片的 `` 元素实现井字棋人机交互逻辑。
在实现井字棋(Tic Tac Toe)时,一个关键需求是:玩家点击后放置 "X",延迟 1 秒后由程序自动在尚未被占用的格子中随机放置 "O"。但原代码中 computerChoice() 被立即执行(而非延迟调用),且 cell.src 的比较逻辑存在根本性缺陷——浏览器会将相对路径自动解析为完整 URL(如 http://localhost/Small_uppercase_letter_X.svg.png),而代码却用短文件名直接比对,导致条件恒为 true,所有元素都被输出。
✅ 正确做法:修复 setTimeout 与 src 判断逻辑
首先,setTimeout 的第一个参数必须是函数引用或匿名函数,而非函数调用结果。原写法 setTimeout(1000, computerChoice()) 实际上是立即执行 computerChoice(),再把其返回值(undefined)传给 setTimeout,完全违背延迟意图。应改为:
setTimeout(() => computerChoice(), 1000); // 推荐箭头函数写法
// 或
setTimeout(function() { computerChoice(); }, 1000);其次,<img> 元素的 src 属性返回的是绝对 URL,不能直接与相对路径字符串(如 "Small_uppercase_letter_X.svg.png")比较。更健壮的方式是提取文件名或使用 getAttribute('src') 获取原始 HTML 值,但更推荐统一使用 dataset 自定义属性来标记状态,避免 URL 解析干扰:
<img data-state="empty" alt="cell">
function manChoice() {
// 防止重复点击已占格子
if (this.dataset.state !== "empty") return;
this.src = "Small_uppercase_letter_X.svg.png";
this.dataset.state = "x";
setTimeout(() => computerChoice(), 1000);
}
function computerChoice() {
const availableCells = Array.from(cells).filter(
cell => cell.dataset.state === "empty"
);
if (availableCells.length === 0) return; // 棋盘已满
const randomCell = availableCells[Math.floor(Math.random() * availableCells.length)];
randomCell.src = "Small_uppercase_letter_O.svg.png";
randomCell.dataset.state = "o";
}⚠️ 注意事项总结
- 永远不要用 src 字符串直等判断状态:浏览器自动补全协议、域名、路径,导致 "X.svg.png" !== element.src 恒成立;
- setTimeout(fn, delay) 中 fn 必须是函数,不是 fn():后者是立即调用并传入返回值;
- 添加防重逻辑:玩家点击前检查 dataset.state,避免多次点击同一格;
- 增强可维护性:用 data-state 统一管理格子状态(empty / x / o),比依赖 src 更清晰、可靠。
通过以上重构,computerChoice() 将真正延迟执行,并精准作用于未被占用的格子,使井字棋逻辑符合预期。

















