Team Stats project: why does the solution add the data to the getter method?

Link to Team Stats project:
https://www.codecademy.com/courses/introduction-to-javascript/projects/team-stats

This project specifically asked us NOT to use setter methods. I would have preferred to practise using them, but I followed the instructions, assuming there must be a good reason for this. What I find very strange though, is the following:

In task 6 of this project, we create the addPlayer method to add a new player object to the array storing all of our player objects (in the property _players. This is where I think a setter (set players) would be appropriate (just like what we used in the previous project, Meal Maker). But, with this option specifically ruled out in task 5, I pushed the new player object directly to the _players property (as in the code below).

However, in the solution it is pushed to the getter get players (as in my comment in the code below). Both work, but it seems very counter-intuitive to me that the solution not only bypasses using a setter but ALSO bypasses the actual ‘stored’ array (in the _players property). Is this actually sound? Is it working by reference, or something similar? Unfortunately, the walk-through video doesn’t make any reference to this point.

const team = {
  _players: [
    {
      firstName: 'Jordi',
      lastName: 'Siscart',
      age: 35
    }, {
    // etc.
    }
  ],
  get players() {
    return this._players
  },
  addPlayer (firstName, lastName, age) {
    const player = {
      firstName,
      lastName,
      age
    }
    this._players.push(player)    // solution:  this.players.push(player)
  }
};

The exact same things happens with the addGame method to add a new game object to the array storing all of our games objects:

My code in addGame():
this._games.push(game) // pushed directly to the stored array in the property _games

Suggested solution:
this.games.push(game) // pushed to the getter get games

(I haven’t given the rest of the code for the games, as the format is the same as with the players i.e. only the identifiers change.)

For what it’s worth, the following would be my preferred solution using setters. I’ve also seen that this is what @mtf suggests in this thread: https://discuss.codecademy.com/t/why-arent-the-getters-referenced-in-the-lessons/376392/2?u=jon_morris

const team = {
  _players: [
    {
      firstName: 'Jordi',
      lastName: 'Siscart',
      age: 35
    }, {
    // etc.
    }
  ],
  get players() {
    return this._players
  },
  set players(player) {
    this._players.push(player) 
  },
  addPlayer (firstName, lastName, age) {
    const player = {
      firstName,
      lastName,
      age
    }
    this.players = player
  }
};

However, I’m really interested to get some expert opinion about why either of the other solutions would be preferrable - especially the suggested solution in the project hints and video, and why it actually works.

4 Likes

Sometimes the solutions just present examples. In this particular project they are hints. If you were to try using this.players.push(player) it would result in an error unless you change _players: to players: (which you don’t want to do) in the team object. Either the hint is wrong or it’s just a hint for you to see the general structure of the method without exactly specifying everything to type.

As I stated using this.players.push(player) would result in an error. You can’t push a value to the array using a getter. The getter is for retrieving information not setting.
Hope this helps!

UPDATE: Apparently I am incorrect. You can use a getter in this manner. I don’t like it, but it’s doable.

1 Like

No, it will not unless it is written that way in a setter where it would create a circular reference. It is perfectly valid to use a getter when setting with another method (not a setter).

this.players.push(new Player(first, last, age));

My code uses a Player class, and a Game class. That can be ignored.


team.addPlayer('Steph', 'Curry', 28);
team.addPlayer('Lisa', 'Leslie', 44);
team.addPlayer('Bugs', 'Bunny', 76);

team.addGame('Sharks', 7, 1);
team.addGame('Kings', 6, 2);
team.addGame('Ducks', 4, 3);

team.roster();
team.results();

Output

Player, Steph Curry added to team.
Player, Lisa Leslie added to team.
Player, Bugs Bunny added to team.
Game, 7 to 1 vs Sharks added to games.
Game, 6 to 2 vs Kings added to games.
Game, 4 to 3 vs Ducks added to games.
Steph Curry 28
Lisa Leslie 44
Bugs Bunny 76
Sharks 1 Us 7
Kings 2 Us 6
Ducks 3 Us 4

FOR THE RECORD

The above referred post incorrectly suggests setters, since I obviously did not read the instructions thoroughly. My own code does not use setters now that I’ve looked at it.

4 Likes

I stand corrected. I tried changing it in mine, and it resulted in an error, but I just tried the code posted by @jon_morris, and it works fine. I re-checked my code, and it’s obvious why it results in an error. I seriously doctored my get players() getter to return a formatted roster instead of simply return this._players.
I share @jon_morris’s opinion though. I don’t like the idea of using a getter in this manner. Seems like an extra step for the computer instead of pushing the value directly to the ._players[] array. The addPlayers() method should take care of refining/validating the data.

1 Like

In my view, having a set players setter which pushes an object onto an array is not a logical use of a setter.

(Only) My reasoning
We’re pushing a value onto an existing array. We’re not replacing its current data.

We’re not saying “I have an existing array, and I’m going to replace that with a whole other array.”

If the code was doing that, then I would say we have a logical use for a setter.


Viewpoint reduces to
Setters are for assigning.

1 Like

@mtf @midlindner @antonjw

Thanks for the replies!..interesting discussion and points of view…

I can see this reasoning - i.e. we’re adding rather than completely reassigning
BUT
if we follow this line of thinking, doesn’t the use of a getter to add this data to the array (as suggested in the solution) become even less intuitive? The directional flow of data TO the property is, after all, the same as with a setter, whereas I have understood the role of a getter to be one of data retrieval FROM the property…

Having said that, we have this interesting comment:

By this, am I right in understanding that a single getter (as in my code above) is actually being used as both a getter (to retrieve) and a “setter” (not in a “strict” sense to replace, but to add)? Is that correct? If we don’t use a setter, and our property is marked private with the underscore, is it then preferable to use a multi-purpose getter to both get and add data, rather than do what I did and push directly to our “private” property using this._players.push(newDataToAdd)

If we get our getter multi-tasking in this way, what I still don’t understand is whether the new player/game data (in the new object created in addPlayer/Game) ever actually ends up in the property, or whether it just hangs around in the getter waiting to hitch a ride with the others when they get retrieved by the getter??

Is this one of those topics that is still too new to have any definitive consensus in the JavaScript community in terms of, not just what works, but what is best practice?

1 Like

No. This is not a setter, but a method that sets a property using the getter to bind the private variable. The reason there is no setter is because the author does not want,

this.players = ....

to be possible. The addPlayer method doesn’t hint to the backing variable even existing. The getter provides this cover, and discreet access.


When there is no setter for a property, then the only reference to the backing variable, _prop will be in the getter, and nowhere else. It follows the getter will not add window dressing to the return.

return this._prop;
2 Likes

And pushing is assigning an element to an array. An argument is also an assignment since we are naming it as a parameter.

The thinking seems to support a setter pushing data to an array, which would go support the fact that arrays as property values has always been valid.

This particular exercise is actually the solution to the argument that the new syntax defies logic…

obj = {
  _prop: [],
  get prop () {
    return this._prop;
  },
  set prop (value) {
    this._prop.push(value);
  }
}
obj.prop = 'value';

It works perfectly, appending the new value to the prop array. Too bad it doesn’t read like it behaves. This is what has some backs up.

Using a helper method rather than a setter assuages the naysayer’s objections. It does not mean we have to give up the privacy of our variables, though, since as shown above we can use the getter at every instance of polling the variable.

obj = {
  _prop: [],
  get prop () {
    return this._prop;
  },
  addProp (value) {
    this.prop.push(value);
  }
}
obj.addProp('value)';

At least now it reads as one might expect and it is a less ambiguous approach. If the getter is unknown, the secret of the private variable’s existence is unknown. The data structure and its values are a little bit safer from encroachment.

4 Likes

Appreciate the clarification on terminology.

I had thought about the adding of array elements as also potentially being ‘assigning’… but oh well, clear enough to which kind of assignment I was referring from my other reasoning :wink:

Which was as per your own reasoning, though I think your intro line to this nails it:

To that point, I take the view that we shouldn’t build functionality that isn’t necessary; i.e. that there is no requirement for.

In the professional world we would be working to requirements. With anything we do over and above requirements, we’re likely to introduce bugs and unexpected behaviour.

2 Likes

Thanks again guys for some very helpful comments. I’m so much clearer on this now :grinning:

I think one of the main things that’s helped me conceptualise what’s actually happening “under the hood”, is the idea that the “data” isn’t actually moving to or from somewhere, but instead is being referenced / polled.

So, this helps me to understand why the getter, whilst not also being a setter in disguise, is able to provide access to (reference/poll) our private property for the purposes of either retrieving or assigning values to it.

Is that about right?.. I don’t want to mislead any other readers by what I’ve just added…

1 Like

That would be about right. Getters give us private access to protected properties. They do not have write permission per se, just access which other methods can channel through to get at the values.

2 Likes

I’ve also just understood this point you made:

If I’ve understood correctly, the getter can be used to retrieve the private property value(s) in more than just one situation (e.g. other than just logging to the console) when it just has the basic return statement return this._prop with no added “frills”.

It’s because of this basic return statement, that our addPlayer method can use the getter to access the “private” array and push the new player into it, all within its own code block…

1 Like

Pretty much. If we dress up the return it will not be the value we return, but a string construct of sorts that we cannot do anything with except print. We want all our methods to be able to access the raw data. Other methods can be written to provide the window dressing we may need to show the user. The main thing is that none of them should have private access and must draw upon the getter for that access.

2 Likes

The ‘not adding window dressing’ in getters was not a principle I was aware of. But really makes sense thanks to @jon_morris’s persistence in asking clarifying questions, and @mtf’s knowledgeable and patient replies.

So much value in these kinds of discussions chaps. Awesome stuff!

3 Likes

If you’re interested, here’s a link to a thread where @mtf and I have had a further discussion about when you can use a getter to access a property marked as private.

https://discuss.codecademy.com/t/faq-classes-review-classes/372000?u=jon_morris
(The discussion starts from the 4th post in this thread - but skip towards the bottom if you want to get straight to the conclusions :wink:)

It turns out that you can use a getter when pushing new data into an array from a method (as we have been discussing in this thread); BUT you CAN’T when making a single assignment to replace the private property’s entire value - in this situation a setter would seem the appropriate choice.

(More details and code examples in the linked thread)

1 Like

The HINT to step 6 confuses me

I tried to comment my questions.
The addPlayer method goes INSIDE or OUTSIDE the team object?
Then step 7 says to go “below” the team object. That means after you close out the team object with }; right? Then, any help with my commented questions would be most appreciated :slight_smile:

It would go inside, else it would not be a method of the team object.


If you wish to make it interesting, add a Player class to global namespace and when adding a player to the team, invoke a new instance object from that class. That way, down the road you can add player stats, and other representations of a Player instance.

 this.players.push(new Player(first, last, age));
1 Like

mtf - thank you for answering so many questions for me…the light bulbs are starting to go on!
Now I see how the braces and brackets are highlighted as pairs (never noticed before) and things are making more sense. I’d like to try to do your challenge posed above, but i don’t think I’ve learned classes yet. I’ll be back !

3 Likes

Howcome we don’t have to put _players? Is it because were referencing get players method? Sorry if that sounds like a dumb question.

1 Like
this.players

accesses the getter, which returns the array assigned to this._players which can have a new value pushed to it. The program should never need to access _players since there is a getter to do that for us. It’s the only real privacy we can offer the backing variable (pseudo-hidden property).

1 Like