JavaScript classes with getter and setter cause RangeError: Maximum call stack size exceeded

Solution 1:

Your "cash" setter calls the "cash" setter, which calls the "cash" setter, which calls the "cash" setter...

Accessing the property setter by its own name inside the setter creates an infinite recursive function call.

Solution 2:

I know I'm late, but I think I can clarify one or two points here:

First, there is the question of privacy, which is a long term discussion in the JavaScript community.

class Player {
   constructor(id) {
      this.cash = 350; // this._cash, alternatively
   }

   get cash() {
      return this.cash;
   }

   set cash(value) {
      this.cash = value;
   }
};

let player1 = new Player();

In this case, this.cash is a public property, so you don't really need a getter and a setter method to handle it, because you can get it with player1.cash and set it with player1.cash = newCash; and it is throwing the error because the getter and the setter are being called recursively, as mentioned by others.

However, if you simply rename the property to this._cash, you have to understand that this IS NOT A PRIVATE PROPERTY. If you try to access player1._cash, you will have access to the property value in the same way you will have with player1.cash.

So, how do we get privacy poperly implemented?

There are 2 main ways of doing this with ES6/ES2015: Using the new primitive type Symbol or using WeakMaps. I'm not going into details about this two new features of the language, but I'll show how this would be implemented in this case.

Using Symbols:

const CASH = Symbol();

class Player {

   constructor () {
      this[CASH] = 350;
   }

   get cash(){
      return this[CASH];
   }

   set cash(cash) {
      this[CASH] = cash;
   }

}

Using WeakMaps

let map =  new WeakMap();

class Player {

   constructor () {
      map.set(this, {
         cash: 350
      });    
   }

   get cash(){
      return map.get(this).cash;
   }

   set cash(cash) {
      map.get(this).cash = cash;
   }

}

IMPORTANT

While the syntax for Symbols are better, it requires native support of the browser to actually work. You can write it with a transpiler but, under the hood, it will mock it to old ES5 standards. The native support for WeakMaps are better and, on the other hand, this feature just plays with the GC and with the enumerable option of the objects properties. So, in the end, it's your choice.

Solution 3:

cash represents the getter/setter, _cash is the 'private' property.

  set cash(value) { // line 19
      this._cash = value; // line 20
  }

Have a look at this page for a clear example.