Вопрос или проблема
Я начинающий программист на JavaScript и проходил онлайн-курс на YouTube. https://www.youtube.com/watch?v=EerdGm-ehJQ&t=9176s (03:19:00)
Я создаю игру “Камень, ножницы, бумага”, используя Math.random. Однако VS Code выдает следующие сообщения:
- Невозможно переопределить переменную блочной области видимости ‘randomNumber’.
- Невозможно переопределить переменную блочной области видимости ‘computerMove’.
- Невозможно переопределить переменную блочной области видимости ‘result’.
Может кто-то объяснить, почему это происходит и какова может быть решение?
Код представлен ниже.
<!DOCTYPE html>
<html>
<head>
<title>Камень Ножницы Бумага</title>
</head>
<body>
<p>Камень Ножницы Бумага</p>
<button
onclick="
const randomNumber = Math.random();
let computerMove="";
if (randomNumber >= 0 && randomNumber < 1 / 3 )
{
computerMove="rock";
}
else if (randomNumber >= 1 / 3 && randomNumber < 2 / 3 )
{
computerMove="paper";
}
else if (randomNumber >= 2 / 3 && randomNumber < 1 )
{
computerMove="scissors";
}
let result="";
if (computerMove === 'rock'){
result="Ничья.";
} else if (computerMove === 'paper' ) {
result="Проиграл.";
} else if (computerMove === 'scissors' ) {
result="Ты выиграл.";
}
alert (` Ты выбрал камень. Компьютер выбрал ${computerMove}. ${result}.`)
"
>
Камень
</button>
<button
onclick="
const randomNumber2 = Math.random();
let computerMove="";
if (randomNumber2 >= 0 && randomNumber2 < 1 / 3 )
{
computerMove2 = 'rock';
}
else if (randomNumber2 >= 1 / 3 && randomNumber2 < 2 / 3 )
{
computerMove2 = 'paper';
}
else if (randomNumber2 >= 2 / 3 && randomNumber2 < 1 )
{
computerMove2 = 'scissors';
}
let result2 = '';
if (computerMove2 === 'rock'){
result2 = 'Ты выиграл.';
} else if (computerMove2 === 'paper' ) {
result2 = 'Ничья.';
} else if (computerMove2 === 'scissors' ) {
result2 = 'Ты проиграл.';
}
alert (` Ты выбрал бумагу. Компьютер выбрал ${computerMove2}. ${result2}.`)
"
>
Бумага
</button>
<button
onclick="
const randomNumber = Math.random();
let computerMove="";
if (randomNumber >= 0 && randomNumber < 1 / 3 )
{
computerMove="rock";
}
else if (randomNumber >= 1 / 3 && randomNumber < 2 / 3 )
{
computerMove="paper";
}
else if (randomNumber >= 2 / 3 && randomNumber < 1 )
{
computerMove="scissors";
}
let result="";
if (computerMove === 'rock'){
result="Ты проиграл.";
} else if (computerMove === 'paper' ) {
result="Ты выиграл.";
} else if (computerMove === 'scissors' ) {
result="Ничья.";
}
alert (` Ты выбрал ножницы. Компьютер выбрал ${computerMove}. ${result}.`);
"
>
Ножницы
</button>
<script></script>
</body>
</html>
Проблема связана с тем, как браузеры интерпретируют встроенные обработчики событий.
Все встроенные обработчики событий разделяют одну и ту же глобальную область видимости (объект window в браузерах). Это означает, что переменные, объявленные с помощью var
, let
или const
внутри любого встроенного обработчика, являются частью глобального контекста, если явно не ограничены в области видимости.
Переменные, объявленные с помощью let
и const
, имеют блочную область видимости, что означает, что они должны быть, в идеале, ограничены блоком {}
, в котором они определены.
Однако в контексте встроенных обработчиков событий каждый обработчик не изолирован внутри своего собственного блока. Вместо этого они рассматриваются так, как будто они являются частью одного и того же общего скрипта, разделяя глобальную область видимости.
Ваш код в конечном итоге определяет область видимости примерно так, поэтому возникает ошибка:
{
const randomNumber = Math.random();
const randomNumber2 = Math.random();
const randomNumber = Math.random();
}
Простое решение: Объявите уникальные переменные для каждого обработчика.
Но ответ будет неполным, если не указать следующее.
Я бы очень рекомендовал избегать реализации встроенных обработчиков событий таким образом, как вы это делаете, если только это не для образовательных целей. Обычно вы хотите разделить реализацию на отдельный JavaScript-скрипт следующим образом:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Камень Ножницы Бумага</title>
</head>
<body>
<h1>Камень Ножницы Бумага</h1>
<button id="rockButton">Камень</button>
<button id="paperButton">Бумага</button>
<button id="scissorsButton">Ножницы</button>
<script src="script.js"></script>
</body>
</html>
script.js
Определите изолированную область видимости функции, введя функцию handlePlayerMove()
и прикрепив функцию в качестве обработчика с помощью addEventListener
JavaScript
// Функция для определения хода компьютера
function getComputerMove() {
const randomNumber = Math.random();
if (randomNumber < 1 / 3) return 'rock';
if (randomNumber < 2 / 3) return 'paper';
return 'scissors';
}
// Функция для определения результата
function determineResult(playerMove, computerMove) {
if (playerMove === computerMove) return 'Ничья.';
if (
(playerMove === 'rock' && computerMove === 'scissors') ||
(playerMove === 'paper' && computerMove === 'rock') ||
(playerMove === 'scissors' && computerMove === 'paper')
) {
return 'Ты выиграл!';
}
return 'Ты проиграл.';
}
// Обработчик события для хода игрока
function handlePlayerMove(playerMove) {
const computerMove = getComputerMove();
const result = determineResult(playerMove, computerMove);
alert(`Ты выбрал ${playerMove}. Компьютер выбрал ${computerMove}. ${result}`);
}
// Привязываем обработчики событий к кнопкам
document.getElementById('rockButton').addEventListener('click', () => handlePlayerMove('rock'));
document.getElementById('paperButton').addEventListener('click', () => handlePlayerMove('paper'));
document.getElementById('scissorsButton').addEventListener('click', () => handlePlayerMove('scissors'));
Это ложное срабатывание, возникающее из VS Code: это не настоящая ошибка, и ваш код будет работать без этой ошибки. VS Code не учитывает, что код, который назначается на HTML-атрибут, такой как onclick
, будет выполняться в своем собственном блоке. Переменная, объявленная в одном таком скрипте, не будет доступна в другом таком скрипте. VS Code упускает эту нюанс и ошибочно трактует это как ошибку.
Но отступив на шаг назад, действительно очень плохая практика помещать длинные блоки кода в атрибуты onclick
(или любые другие HTML-атрибуты). На самом деле, это лучшая практика — полностью избегать этого и прикреплять обработчики событий через код, а не через HTML.
Другие замечания по вашему коду:
-
randomNumber >= 0
всегда истина.Math.random()
гарантирует возврат числа от 0 до 1 (1 не включительно). -
Вместо того чтобы делать проверки, такие как
randomNumber < 1 / 3
, проще умножить случайное число на 3, округлить его до целого числа и затем использовать это целое число (которое равно 0, 1 или 2) как индекс для выбора одной из трех опций. -
Вы повторяете почти один и тот же код три раза. Это можно избежать, используя один и тот же код для реагирования на три возможных нажатия кнопок.
-
Вместо того чтобы сравнивать строки, будет проще применить логику к целым числам (которые равны 0, 1 или 2).
-
Логика этой игры действительно определяется модульной арифметикой. Она полностью определяется выражением playerMove – computerMove (mod 3).
Существует много способов реализовать это, но для вашего вопроса самым важным изменением является использование функции и размещение ее в script
, а также ее вызов при нажатии кнопки пользователем.
Пример того, как вы могли бы это сделать:
const choices = ["rock", "paper", "scissors"];
const outcomes = ["Ничья", "Ты выиграл", "Ты проиграл"];
function play(e) {
if (e.target.tagName !== "BUTTON") return; // Клик не был на кнопку
// Получите как ход игрока, так и ход компьютера в виде целого числа из {0, 1, 2}.
const playerMove = +e.target.id; // Это номер кнопки, на которую нажали
const computerMove = Math.floor(Math.random() * 3);
// Логика выигрыша действительно является вопросом модульной арифметики:
const result = (3 + playerMove - computerMove) % 3;
alert (`Ты выбрал ${choices[playerMove]}. Компьютер выбрал ${choices[computerMove]}. ${outcomes[result]}.`)
}
document.addEventListener("click", play); // Привязка здесь, а не в HTML
<button id="0">Камень</button>
<button id="1">Бумага</button>
<button id="2">Ножницы</button>
</div>
Ответ или решение
Ошибки переменных и конструкции If в JavaScript: анализ и решение
Проблема, с которой вы столкнулись при разработке игры "камень, ножницы, бумага" на JavaScript, связана с ошибками переопределения переменных. Давайте более подробно рассмотрим вашу ситуацию, выявим причины ошибок и предложим эффективные решения.
Причины ошибок
-
Область видимости переменных: JavaScript использует блочную область видимости для переменных, объявленных с помощью
let
иconst
. Однако, когда вы используете их в обработчиках событий внутри HTML-атрибутов (например,onclick
), они фактически делятся одной областью глобального видимости. Когда вы пытаетесь повторно объявить переменныеrandomNumber
,computerMove
, илиresult
, это приводит к ошибкам, так как одна из переменных уже существует в этой сфере. -
Ошибка в использовании переменных: При использовании переменной, например, в одном из ваших обработчиков, вы столкнулись с ошибкой, так как каждая переменная имеет одинаковое имя в разных обработчиках, что вызывает их конфликт.
Решения
Чтобы исправить указанные ошибки, рекомендуется:
-
Избежать использования inline обработчиков событий: Вместо определения обработчиков непосредственно в HTML, создайте их в отдельном JavaScript-файле или в теге
<script>
внизу вашего HTML-документа. Это поможет избежать путаницы с областями видимости.Пример структуры файлов:
index.html:
<!DOCTYPE html> <html> <head> <title>Камень, Ножницы, Бумага</title> </head> <body> <h1>Камень, Ножницы, Бумага</h1> <button id="rockButton">Камень</button> <button id="paperButton">Бумагa</button> <button id="scissorsButton">Ножницы</button> <script src="script.js"></script> </body> </html>
script.js:
function getComputerMove() { const randomNumber = Math.random(); if (randomNumber < 1 / 3) return 'rock'; if (randomNumber < 2 / 3) return 'paper'; return 'scissors'; } function determineResult(playerMove, computerMove) { if (playerMove === computerMove) return 'Ничья.'; if ( (playerMove === 'rock' && computerMove === 'scissors') || (playerMove === 'paper' && computerMove === 'rock') || (playerMove === 'scissors' && computerMove === 'paper') ) { return 'Вы выиграли!'; } return 'Вы проиграли.'; } function handlePlayerMove(playerMove) { const computerMove = getComputerMove(); const result = determineResult(playerMove, computerMove); alert(`Вы выбрали ${playerMove}. Компьютер выбрал ${computerMove}. ${result}`); } document.getElementById('rockButton').addEventListener('click', () => handlePlayerMove('rock')); document.getElementById('paperButton').addEventListener('click', () => handlePlayerMove('paper')); document.getElementById('scissorsButton').addEventListener('click', () => handlePlayerMove('scissors'));
-
Уникальность переменных: Обратите внимание на наименование переменных внутри функции. Не переименовывайте переменные в разных функциях таким образом, чтобы они совпадали. Это может привести к путанице и ошибкам.
Заключение
Избегая использования inline обработчиков и применяя функции для обработки событий, вы не только устраняете ошибки, связанные с повторным объявлением переменных, но и улучшаете читаемость и управляемость вашего кода. Применение таких принципов как разделение кода по файлам и использование модульного подхода в кодировании значительно повысит качество вашего программного обеспечения.
Такие практики не только помогают избежать ошибок, но и обеспечивают удобство сопровождения и масштабирования ваших проектов в будущем.