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

前端 未结 5 445
南方客
南方客 2020-11-30 11:12

I am currently experimenting with ECMA6 classes. My current class looks like the following

class Player {
  constructor(id) {
    this.id = id;
    this.cash         


        
相关标签:
5条回答
  • 2020-11-30 11:32

    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.

    0 讨论(0)
  • 2020-11-30 11:33

    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.

    0 讨论(0)
  • 2020-11-30 11:34

    You are calling recursively your getter.

    It follows a possible alternative:

    class Player {
        constructor(id) {
            this.id = id;
            this._cash = 350;
        }
    
        get cash() {
            return this._cash;
        }
    
        set cash(value) {
            this._cash = value;
        }
    };
    

    Another one using Object.defineProperty:

    class Player {
        constructor(id) {
            this.id = id;
    
            var _cash = 350;
            Object.defineProperty(this, 'cash', {
                get: function() {
                    return _cash;
                }
    
                set: function(v) {
                    _cash = v;
                }
            });
        }
    };
    
    0 讨论(0)
  • 2020-11-30 11:43

    Get & Set ES6 classes brings a new syntax for getters and setters on object properties. Get and set allows us to run code on the reading or writing of a property. ES5 had getters and setters as well but was not widely used because of older IE browsers. ES5 getters and setters did not have as nice of a syntax that ES6 brings us. So lets create a get and set for our name property.

    Source: JavaScript ES6 Class Syntax

    Example:

    // ES6 get and set
    class Person {
        constructor(name) {
            this._name = name;
        }
    
        get name() {
            return this._name.toUpperCase();
        }
    
        set name(newName) {
            this._name = newName;   // validation could be checked here such as only allowing non numerical values
        }
    
        walk() {
            console.log(this._name + ' is walking.');
        }
    }
    
    let bob = new Person('Bob');
    console.log(bob.name);  // Outputs 'BOB'
    
    0 讨论(0)
  • 2020-11-30 11:48

    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.

    0 讨论(0)
提交回复
热议问题