猜拳游戏是前端入门常练的小项目,但多数人只实现了基础出拳比对,忽略了计分准确性与逻辑清晰度。本文从零构建一个具备完整计分、输入容错和逻辑解耦的 JavaScript 猜拳游戏,帮助你写出更易维护的代码。

一、核心规则与数据建模
猜拳的本质是三种出拳类型的循环克制关系:石头胜剪刀、剪刀胜布、布胜石头。如果直接写大量 if 判断,不仅冗长还容易漏掉平局。更好的方式是用对象描述胜负映射,把规则和数据分离。
下面用一个 winnerMap 来记录「玩家出拳」对应「能击败的电脑出拳」。这样判断时只需查表,不必嵌套条件。同时我们用一个 scores 对象保存玩家、电脑与平局的次数,方便随时渲染。
// 出拳类型统一用小写
const MOVES = ['rock', 'scissors', 'paper'];
// 玩家出拳能击败的电脑出拳
const winnerMap = {
rock: 'scissors',
scissors: 'paper',
paper: 'rock'
};
// 计分板
const scores = {
player: 0,
computer: 0,
draw: 0
};
二、输入容错与电脑出拳
真实用户可能输入 Rock、ROCK 甚至多余空格,若不做归一化,比对必然失败。因此在接收输入后,应先 trim 并转小写,再用 includes 或精确匹配校验合法性。
电脑出拳则用 Math.random 从 MOVES 数组中随机取一项。把这一步封装成 getComputerMove 函数,既利于测试,也避免主流程杂乱。以下代码展示了带容错的玩家输入处理与电脑随机逻辑。
function normalizeInput(raw) {
const val = raw.trim().toLowerCase();
if (!MOVES.includes(val)) {
throw new Error('请输入 rock、scissors 或 paper');
}
return val;
}
function getComputerMove() {
const idx = Math.floor(Math.random() * MOVES.length);
return MOVES[idx];
}
三、胜负判定与计分逻辑优化
判定函数应当只关心「谁赢」,不关心页面怎么显示,这就是逻辑优化中的单一职责原则。我们写 calcResult 接收双方出拳,返回 'player'、'computer' 或 'draw'。
计分则交给 updateScore,根据结果给对应字段加一。这样若以后要加「连胜奖励」或「局数上限」,只改这两个函数即可,主流程不受影响。下面是具体实现:
function calcResult(player, computer) {
if (player === computer) return 'draw';
if (winnerMap[player] === computer) return 'player';
return 'computer';
}
function updateScore(result) {
if (result === 'player') scores.player++;
else if (result === 'computer') scores.computer++;
else scores.draw++;
}
四、完整对局与渲染示例
将前面模块组合,就能跑一局完整游戏。我们用 playRound 串起输入、电脑出拳、判定与计分,最后把分数输出到控制台或页面节点。
对比早期把所有逻辑写在一个函数里的做法,这种拆分让每部分都能单独复用。例如 calcResult 可直接用于单元测试,而不依赖 DOM。下面的示例演示一次对局并打印计分板:
function playRound(rawInput) {
let player;
try {
player = normalizeInput(rawInput);
} catch (e) {
console.log(e.message);
return;
}
const computer = getComputerMove();
const result = calcResult(player, computer);
updateScore(result);
console.log('你出: ' + player + ',电脑出: ' + computer);
console.log('本局结果: ' + result);
console.log('当前比分 玩家' + scores.player + ' - 电脑' + scores.computer + ' (平' + scores.draw + ')');
}
// 模拟两次出拳
playRound('Rock');
playRound('paper');
五、进一步优化方向
当前方案已解决计分与基础容错,但若要产品化,还可加入历史记录数组、最佳连胜统计,以及用 <button> 替代文本输入避免非法值。注意这里提到的 <button> 是标签名,在正文描述中已转义。
另一个常见优化是把 scores 与页面元素绑定,用函数 render 统一更新 DOM,而非在多处手动改 textContent。这样数据驱动视图,逻辑与界面彻底解耦,后续扩展就不会牵一发而动全身。
JavaScript猜拳游戏逻辑优化修改时间:2026-08-06 00:57:12