Ошибки в операторе if и переменных в JavaScript

Вопрос или проблема

Я начинающий программист на JavaScript и проходил онлайн-курс на YouTube. https://www.youtube.com/watch?v=EerdGm-ehJQ&t=9176s (03:19:00)

Я создаю игру “Камень, ножницы, бумага”, используя Math.random. Однако VS Code выдает следующие сообщения:

  1. Невозможно переопределить переменную блочной области видимости ‘randomNumber’.
  2. Невозможно переопределить переменную блочной области видимости ‘computerMove’.
  3. Невозможно переопределить переменную блочной области видимости ‘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, связана с ошибками переопределения переменных. Давайте более подробно рассмотрим вашу ситуацию, выявим причины ошибок и предложим эффективные решения.

Причины ошибок

  1. Область видимости переменных: JavaScript использует блочную область видимости для переменных, объявленных с помощью let и const. Однако, когда вы используете их в обработчиках событий внутри HTML-атрибутов (например, onclick), они фактически делятся одной областью глобального видимости. Когда вы пытаетесь повторно объявить переменные randomNumber, computerMove, или result, это приводит к ошибкам, так как одна из переменных уже существует в этой сфере.

  2. Ошибка в использовании переменных: При использовании переменной, например, в одном из ваших обработчиков, вы столкнулись с ошибкой, так как каждая переменная имеет одинаковое имя в разных обработчиках, что вызывает их конфликт.

Решения

Чтобы исправить указанные ошибки, рекомендуется:

  1. Избежать использования 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'));
  2. Уникальность переменных: Обратите внимание на наименование переменных внутри функции. Не переименовывайте переменные в разных функциях таким образом, чтобы они совпадали. Это может привести к путанице и ошибкам.

Заключение

Избегая использования inline обработчиков и применяя функции для обработки событий, вы не только устраняете ошибки, связанные с повторным объявлением переменных, но и улучшаете читаемость и управляемость вашего кода. Применение таких принципов как разделение кода по файлам и использование модульного подхода в кодировании значительно повысит качество вашего программного обеспечения.

Такие практики не только помогают избежать ошибок, но и обеспечивают удобство сопровождения и масштабирования ваших проектов в будущем.

Оцените материал
Добавить комментарий

Капча загружается...