web-dev-qa-db-fra.com

Les classes JavaScript avec getter et setter provoquent RangeError: la taille maximale de la pile d'appels est dépassée

J'expérimente actuellement avec les classes ECMA6. Ma classe actuelle ressemble à ce qui suit

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

  get cash() {
    return this.cash;
  }

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

Lorsque je crée maintenant un nouvel objet en appelant let playerObject = new Player(1); je reçois l'erreur suivante

...\node_modules\mysql\lib\protocol\Parser.js:82
        throw err;
              ^
RangeError: Maximum call stack size exceeded
    at Player.cash (player.js:19:11)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
    at Player.cash (player.js:20:15)
Press enter to exit

Qu'est-ce que cela a à voir avec la bibliothèque mysql? Pourquoi l'erreur se produit-elle plusieurs fois sur la même ligne? Je ne l'appelle qu'une seule fois.

25
Cludch

Votre setter "cash" appelle le setter "cash", qui appelle le setter "cash", qui appelle le setter "cash" ...

L'accès au setter de propriétés par son propre nom à l'intérieur du setter crée un appel de fonction récursif infini.

26
Greg Burghardt

Je sais que je suis en retard, mais je pense pouvoir clarifier un ou deux points ici:

Tout d'abord, il y a la question de la confidentialité , qui est une discussion à long terme dans la communauté JavaScript.

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();

Dans ce cas, this.cash est une propriété publique , donc vous n'avez pas vraiment besoin d'un getter et d'une méthode setter pour le gérer, car vous pouvez obtenez-le avec player1.cash et définissez-le avec player1.cash = newCash ; et il jette l'erreur parce que le getter et le setter sont appelés récursivement, comme mentionné par d'autres.

Cependant, si vous renommez simplement la propriété en this._cash , vous devez comprendre que cela N'EST PAS UN PRIVÉ PROPRIÉTÉ . Si vous essayez d'accéder à player1._cash , vous aurez accès à la valeur de la propriété de la même manière que vous aurez avec player1.cash .

Donc, comment pouvons-nous implémenter la confidentialité poperly?

Il y a 2 façons principales de le faire avec ES6/ES2015: Utiliser le nouveau type primitif Symbol ou utiliser WeakMaps. Je n'entre pas dans les détails de ces deux nouvelles fonctionnalités du langage, mais je vais montrer comment cela serait implémenté dans ce cas.

Utilisation de symboles:

const CASH = Symbol();

class Player {

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

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

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

}

Utilisation de 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

Bien que la syntaxe des symboles soit meilleure, elle nécessite une prise en charge native du navigateur pour fonctionner réellement. Vous pouvez l'écrire avec un transpilateur mais, sous le capot, il se moquera des anciennes normes ES5. La prise en charge native de WeakMaps est meilleure et, d'autre part, cette fonctionnalité ne joue qu'avec le GC et avec l'option énumérable des propriétés des objets. Donc, en fin de compte, c'est votre choix.

18

l'argent représente le getter/setter, _cash est la propriété "privée".

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

Jetez un oeil à cette page pour un exemple clair.

4
htatche

Vous appelez récursivement votre getter.

Il s'ensuit une alternative possible:

class Player {
    constructor(id) {
        this.id = id;
        this._cash = 350;
    }

    get cash() {
        return this._cash;
    }

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

Un autre utilisant 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;
            }
        });
    }
};
4
skypjack

Les classes Get & Set ES6 apportent une nouvelle syntaxe pour les getters et les setters sur les propriétés des objets. Get and set nous permet d'exécuter du code lors de la lecture ou de l'écriture d'une propriété. ES5 avait aussi des getters et des setters mais n'était pas largement utilisé en raison des anciens navigateurs IE. Les getters et setters ES5 n'avaient pas la gentille syntaxe qu'ES6 nous apporte. Donc, créons un get et défini pour notre propriété de nom.

Source: Syntaxe de classe JavaScript ES6

Exemple :

// 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
d.danailov