Home > database >  Why does my function fail to return/initialize a factory function object if I use let or const in Ja
Why does my function fail to return/initialize a factory function object if I use let or const in Ja

Time:12-30

My function will successfully initialize/return my factory function object (PlayerObject) as long as I omit the let or const keywords. This seems like a bad idea. I could just move on with my program but it is really bothering me that I don't understand why this happening. I am building a very simple tic tact toe app here.

I will give my html and script here below to show it working to initialize the factory function object (PlayerObject) without using let or const and then I'll show another snippet which demonstrates the failure of the function to initialize the factory function object. If there is a kind old wizened code soul out there who could help me understand what is going on I would really appreciate it.

Below is the snippet that works to create two PlayerObjects and store them in player1 and player2 variables. I just fill in the simple forms with player names number of games and each player chooses x or o. This is super bare-bones code but it works. (As long as neither let or const is used to initialize the PlayerObject.)

const PlayerObject = (nameChoice, symbol) => {
  scoreCount = 0 // score is when a game is won
  tieCount = 0 // tie is when no one wins a game
  return {
    nameChoice, //the name object will be filled by an html form
    symbol, // will be chosen by the game flow
    scoreCount,
    tieCount
  };
}

function newGameConfig() {
  let numberOfGames = document.querySelector('.numberOfRoundsChoice').value
  let player1Name = document.querySelector('.p1Name').value
  let player2Name = document.querySelector('.p2Name').value
  let p1Symbol = document.querySelector('input[name="p1xOrO"]:checked').value //these check for the checked radio button plater inputs
  let p2Symbol = document.querySelector('input[name="p2xOrO"]:checked').value

  player1 = PlayerObject(player1Name, p1Symbol)
  player2 = PlayerObject(player2Name, p2Symbol)

  document.querySelector('.player1Inputs').reset();
  document.querySelector('.player2Inputs').reset(); //reset form values on press of start button
  document.querySelector('.gameNumberChoiceContainer').reset(); //make gameNumbersChoice form reset

  return {
    player1,
    player2
  }
}

//********** EVENT LISTENER ***********//

const startButton = document.querySelector('.startGameButton') // button that starts the whole game and gets player input values

startButton.addEventListener('click', (event) => {
    event.preventDefault()
    newGameConfig()
  }

) // adds all the form info into the factory function object and returns two PlayerObjects
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Tic Tac Toe</title>

</head>

<body>

  <header class='headerTitleContainer'>
    <h1>Tic Tac Toe</h1>
  </header>

  <section>
    <article class='startGameButtonContainer'>


      <div>
        <form class='gameNumberChoiceContainer'>
          <!--Start button and labels and inputs will be replaced by number of games 
                left to play and how many have been played-->
          <label for="gameNumberInput">How Many Games?</label>
          <input type="number" name="gameNumberInput" class='numberOfRoundsChoice'>

        </form>


    </article>


    <article class='playerNamesConfigContainer'>
      <!--Names will be replaced by h1 after being entered.-->

      <div class='playerNameInputs'>
        <form class='player1Inputs'>
          <div>
            <label for='player1Name'>Player 1 Name is...</label>
            <input type='text' name='player1Name' class='p1Name'>
          </div>

          <label for='p1xOrO'>X or O?<label>
                <label for = 'p1xOrO'>X<label>
            <input type = 'radio' name = 'p1xOrO' value = 'X'>
            <label for = 'p1xOrO'>O<label>
                <input type = 'radio' name = 'p1xOrO' value = 'O'>
            </form>
            
          <form class = 'player2Inputs'>
            <div>
            <label for ='player2Name'>Player 2 Name is...</label>
          <input type='text' name='player2Name' class='p2Name'>
          <div>

            <label for='p2xOrO'>X or O?<label>
                <label for = 'p2xOrO'>X<label>
            <input type = 'radio' name = 'p2xOrO' value = 'X'>
            <label for = 'p2xOrO'>O<label>
                <input type = 'radio' name = 'p2xOrO' value = 'O'>
                
            </div>
        </form>

        </div>
        <button class = 'startGameButton'>START NEW GAME </button>
      </div>

        </article>

    </section>

    <section>

        <article>
            <div>
                <p>Player 1 Score</p> 
                <p>Score: </p> 
                <p>Tie: </p>
            </div>

            <div>
                <p>Player 2 Score</p> 
                <p>Score: </p> 
                <p>Tie: </p>
            </div>
                
        </article>

    </section>

</body>
</html>

Here is the code that does not work to create two PlayerObjects if I try to initialize them with let or const. Here I show this with const.

const PlayerObject = (nameChoice, symbol) => {
  scoreCount = 0 // score is when a game is won
  tieCount = 0 // tie is when no one wins a game

  return {
    nameChoice, //the name object will be filled by an html form
    symbol, // will be chosen by the game flow
    scoreCount,
    tieCount
  };
}

function newGameConfig() {
  let numberOfGames = document.querySelector('.numberOfRoundsChoice').value
  let player1Name = document.querySelector('.p1Name').value
  let player2Name = document.querySelector('.p2Name').value
  let p1Symbol = document.querySelector('input[name="p1xOrO"]:checked').value //these check for the checked radio button plater inputs
  let p2Symbol = document.querySelector('input[name="p2xOrO"]:checked').value

  const player1 = PlayerObject(player1Name, p1Symbol)
  const player2 = PlayerObject(player2Name, p2Symbol)

  document.querySelector('.player1Inputs').reset();
  document.querySelector('.player2Inputs').reset(); //reset form values on press of start button
  document.querySelector('.gameNumberChoiceContainer').reset(); //make gameNumbersChoice form reset

  return {
    player1,
    player2
  }
}

//********** EVENT LISTENER ***********//

const startButton = document.querySelector('.startGameButton') // button that starts the whole game and gets player input values

startButton.addEventListener('click', (event) => {
    event.preventDefault()
    newGameConfig()
  }

) // adds all the form info into two PlayerObject factory function objects.
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Tic Tac Toe</title>

</head>

<body>

  <header class='headerTitleContainer'>
    <h1>Tic Tac Toe</h1>
  </header>

  <section>
    <article class='startGameButtonContainer'>


      <div>
        <form class='gameNumberChoiceContainer'>
          <!--Start button and labels and inputs will be replaced by number of games 
                left to play and how many have been played-->
          <label for="gameNumberInput">How Many Games?</label>
          <input type="number" name="gameNumberInput" class='numberOfRoundsChoice'>

        </form>


    </article>


    <article class='playerNamesConfigContainer'>
      <!--Names will be replaced by h1 after being entered.-->

      <div class='playerNameInputs'>
        <form class='player1Inputs'>
          <div>
            <label for='player1Name'>Player 1 Name is...</label>
            <input type='text' name='player1Name' class='p1Name'>
          </div>

          <label for='p1xOrO'>X or O?<label>
                <label for = 'p1xOrO'>X<label>
            <input type = 'radio' name = 'p1xOrO' value = 'X'>
            <label for = 'p1xOrO'>O<label>
                <input type = 'radio' name = 'p1xOrO' value = 'O'>
            </form>
            
          <form class = 'player2Inputs'>
            <div>
            <label for ='player2Name'>Player 2 Name is...</label>
          <input type='text' name='player2Name' class='p2Name'>
          <div>

            <label for='p2xOrO'>X or O?<label>
                <label for = 'p2xOrO'>X<label>
            <input type = 'radio' name = 'p2xOrO' value = 'X'>
            <label for = 'p2xOrO'>O<label>
                <input type = 'radio' name = 'p2xOrO' value = 'O'>
                
            </div>
        </form>

        </div>
        <button class = 'startGameButton'>START NEW GAME </button>
      </div>

        </article>

    </section>

    <section>

        <article>
            <div>
                <p>Player 1 Score</p> 
                <p>Score: </p> 
                <p>Tie: </p>
            </div>

            <div>
                <p>Player 2 Score</p> 
                <p>Score: </p> 
                <p>Tie: </p>
            </div>
                
        </article>

    </section>
 
</body>
</html>

Is this a problem that I should be concerned with? I thought it was customary to always initialize variables/objects with let or const?

Thank you for your time.

If I did not initialize the PlayerObject factory function with let or const like player1 = PlayerObject('name','O') my eventListener would fire off the newGameConfig() function and I could create two PlayerObject objects with form info supplied. If I tried const player1 = PlayerObject('name','X') the object would never get created. I am trying to understand why this works the way it is. This is all in JavaScript.

edit: also I should add that no error occurs in the console when the object fails to be created when const or let is used...

CodePudding user response:

In the non-working code, player1 and player2 are local to the newGameConfig() function. Presumably the rest of the game code needs to use those player objects. You return them with

  return {
    player1,
    player2
  }

but the code that calls newGameConfig() doesn't assign these returned variables anywhere.

Since it's being called inside another function (the start button event listener), assigning them to variables there would have the same problem.

While global variables should generally be avoided, sometimes it makes best sense to use them. If your game design requires this kind of global state, you can just stick with what you're doing in the first version. Declare the variables at top-level with:

let player1, player2;

so that it will work as intended in strict mode.

In this case there's not much point in returning the above object.

  • Related