I have a question about getters

I am doing a Codecademy project (Team Stats) and in step 6 it tells you to create a method ‘addPlayer’ so you can later push it what that method did to an array, but why does it work if i have a getter named ‘players’ and my array is named ‘_player’ and i pushed it to ‘players’ getter and it worked anyway, shoudln’t it only work if i push it to the ‘_players array’.
Example:

const team = {
  _players: [
    {
      firstName: 'Pablo',
      lastName: 'Sanchez',
      age: 11
    },
    {
      firstName: 'Camilo',
      lastName: 'Gurria',
      age: 17
    },
    {
      firstName: 'Salvador',
      lastName: 'Dali',
      age: 102
    }
  ],
  _games: [
    {
      opponent: 'The Reds',
      teamPoints: 1,
      opponentPoints: 30
    },
    {
      opponent: 'The Browns',
      teamPoints: 5.3,
      opponentPoints: 40
    },
    {
      opponent: 'The Whites',
      teamPoints: 10,
      opponentPoints: 69
    }
  ],
  get players() {
    return this._players;
  },
  get games() {
    return this._players;
  },
  addPlayer(firstName, lastName, age) {
    let player = {
      firstName: firstName,
      lastName: lastName,
      age: age
    };
    this.players.push(player);
  }
};
team.addPlayer('Steph', 'Curry', 28);
team.addPlayer('Lisa', 'Leslie', 44);
team.addPlayer('Bugs', 'Bunny', 76);

console.log(team.players);

Should it not only work if it is pushed to ‘_players’

const team = {
  _players: [
    {
      firstName: 'Pablo',
      lastName: 'Sanchez',
      age: 11
    },
    {
      firstName: 'Camilo',
      lastName: 'Gurria',
      age: 17
    },
    {
      firstName: 'Salvador',
      lastName: 'Dali',
      age: 102
    }
  ],
  _games: [
    {
      opponent: 'The Reds',
      teamPoints: 1,
      opponentPoints: 30
    },
    {
      opponent: 'The Browns',
      teamPoints: 5.3,
      opponentPoints: 40
    },
    {
      opponent: 'The Whites',
      teamPoints: 10,
      opponentPoints: 69
    }
  ],
  get players() {
    return this._players;
  },
  get games() {
    return this._players;
  },
  addPlayer(firstName, lastName, age) {
    let player = {
      firstName: firstName,
      lastName: lastName,
      age: age
    };
    this._players.push(player);
  }
};
team.addPlayer('Steph', 'Curry', 28);
team.addPlayer('Lisa', 'Leslie', 44);
team.addPlayer('Bugs', 'Bunny', 76);

console.log(team.players);

P.S. I watched the tutorial and that didn’t answer my question, please help

here:

this.players.push(player);

this.players will call the getter, which returns this._players, which is the array, so the item is appended to the array

3 Likes

Thank you very much for responding, much apreciated. I didn’t really get it, I feel I’m starting to, but not completely. Could you please go a little bit more in depth or re- word it a little different?
Thanks

I could, but i would first recommend you to use this tool:

http://pythontutor.com/javascript.html#mode=edit

it allows you to step through your code execution. This should really help you understand the flow of your program (and the getter)

if after that, you still have question, feel free to ask. I will answer them

strange as it may sound, the more you do yourself, the better.

1 Like

Thank you very much for the tool, I had no idea about this tool and it seems really useful but I tried to understand it and I still dont understand it.

Something to consider in terms of pseudo-privacy…

this.players.push(player)     // public

    vs.

this._players.push(player)    // private (by all intents)

The idea being that only setters or getters ever access the backing variable. The addPlayer method is not a setter, (not a special method) so it should not access backing variables. The first example does as @stetim94 describes, invoke the accessor (getter) to establish a binding to the actual property and thus its value, the array.

Bottom line, choose the public approach in any method (that is not set prop () {}) that effects change to property values.


Extra Study

First off, there is nothing wrong with the code presented in the sample, especially the first example. However, it behooves us to explore some of the ins and outs, and also some of the syntactic sugar of ES6. Below is not a recommendation as much as an exploration.

team = {
  _players: [],
  get players () {
    return this._players
  },
  set players (player) {
    [first, last, age] = player
    this._players.push(this.instance(first, last, age))
  },
  instance (first, last, age) {
    return {firstName: first, lastName: last, age: age}
  }
}
 > team.players = ['Mike', 'Johnstone', 21]
<- (3) ["Mike", "Johnstone", 21]
 > team.players = ['Theo', 'Fleury', 22]
<- (3) ["Theo", "Fleury", 22]
 > team.players.forEach(x => console.log(x))
<- > {firstName: "Mike", lastName: "Johnstone", age: 21}
   > {firstName: "Theo", lastName: "Fleury", age: 22}
<- undefined

Supplemental

Now we get to the truly nifty stuff…

 > team = {
     _players: [],
     get players () {
       return this._players
     },
     set players (player) {
       this._players.push(this.factory(player))
     },
     factory (player) {
       [firstName, lastName, age] = player
       return {firstName, lastName, age}
     }
   }
 > 
team.players = ['Mike', 'Johnstone', 21]
team.players = ['Theo', 'Fleury', 22]
team.players.forEach(x => console.log(x))
{firstName: "Mike", lastName: "Johnstone", age: 21}
{firstName: "Theo", lastName: "Fleury", age: 22}

factory is still a public method, but we treat it as a private one.

Thanks @mtf but is still isn’t that clear to me. Could you maybe walk me through what happens a bit more? Like how it invokes it and how it establishes a bond between the getter and array? I also have another question is this

team.players = ['Mike', 'Johnstone', 21]

the same as using .push()

Thank you very much.

1 Like

No, not really. The push method is invoked by the setter.

A setter can take only one argument, so ours is packaged as an array so all three values can be passed in. It’s the assignment that invokes the setter.

In the setter, we take a single parameter, player, which we know to be an array of three elements. As the argument for push we call the local factory function and pass in the player array.

In the factory we deconstruct the array into its three values and give them variable names. We then use those names as properties and pair them with their values in an object, which is returned to the setter’s push statement. That gives us the object we see in the print out.

When we invoke a setter, it in turn invokes the getter, but only so that it can create a temporary binding that prevents the property from being accessed while the setter is working. That’s the magic going on in the background and is managed by ES. It’s a bit mysterious, I know. As much as I’ve tried to wrap my head around it, there is still some confusion. All I know is it works as long as we play by the rules.

Note that we can have only getters in our object/class without using setters. If we do use setters, they must each have a corresponding getter or they won’t work. Above, factory is a helper function that we use to generate an object. We could very well have done all that inside the setter, but I liked the separation of concerns offered by the helper.

So, I now understand the code you had previously posted with your explanation, thanks a lot! And if i understood correctly my method ‘addPlayer()’ kind of worked as the setter so it invoked the getter. But I still dont get how I pushed something to my property with the getter, but your saying the reason why is a bit fuzzy and you dont know, so for the time being I should just do it. Right?
Thank you very much @mtf !

You didn’t. It was the setter that implemented the push method. All the getter does is tie up the property so it cannot be accessed while it is being set.

As far as your addPlayer method being a setter, that would be incorrect. Yes, it does alter the value, but it does not utilize the special set method. It uses the getter to resolve the backing variable, is all.

Ok, thanks. But my question then is, how did the getter access the setter if in the code I posted I had no setter?
Thanks!

this.players.push(player)

The above invokes the getter which returns the variable to accept the pushed object.

A setter should not behave this way. Make a method instead, named addPlayer

You two are probably talking about different code

by getting the array, and then modifying it
you’re not pushing to your property, you’re pushing to the value that your property refers to (mixing up variable and value) If you hand your value to somebody else then they can modify it all they want (assuming it can be modified (is mutable))

If you try it you’ll find that you are not allowed to do it. There is no set access to that property.

> Object.getOwnPropertyDescriptor(team, 'players')
{
  get: [Function: get players],
  set: undefined,            <------ not present
  enumerable: true,
  configurable: true
}

replacing and modifying are also very different things, it doesn’t match push’s semantics - add another value to list vs hey use this list instead

1 Like