Подтвердить что ты не робот

Классы JavaScript с геттером и установщиком вызывают RangeError: превышен максимальный размер стека вызовов

В настоящее время я экспериментирую с классами ECMA6. Мой текущий класс выглядит следующим образом

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
  }
};

Когда я создаю новый объект, вызывая let playerObject = new Player(1);, я получаю следующую ошибку

...\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

Что это связано с библиотекой mysql? Почему ошибка повторяется в одной строке? Я только называю это один раз.

4b9b3361

Ответ 1

Ваш "денежный" сеттер вызывает "денежный" сеттер, который называет "денежный" сеттер, который вызывает "денежный" сеттер...

Доступ к настройщику свойств по собственному имени внутри сеттера создает вызов бесконечной рекурсивной функции.

Ответ 2

Я знаю, что опаздываю, но я думаю, что могу пояснить один или два пункта:

Во-первых, существует вопрос о конфиденциальности, который является долговременным обсуждением в сообществе 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();

В этом случае this.cash является общедоступным свойством, поэтому вам не нужен метод getter и setter, потому что вы можете получить его с помощью player1. cash и установите его с помощью player1.cash = newCash; и это бросает ошибку, потому что геттер и сеттер называются рекурсивно, как упоминалось другими.

Однако, если вы просто переименуете свойство в this._cash, вы должны понимать, что этот НЕ ЧАСТНАЯ СОБСТВЕННОСТЬ. Если вы попытаетесь получить доступ к player1._cash, у вас будет доступ к значению свойства так же, как у вас будет с player1.cash.

Итак, как мы получим конфиденциальность, реализованную по-польски?

Есть два основных способа сделать это с ES6/ES2015: используя новый примитивный тип Symbol или используя WeakMaps. Я не буду вдаваться в подробности об этих двух новых функциях языка, но я покажу, как это будет реализовано в этом случае.

Использование символов:

const CASH = Symbol();

class Player {

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

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

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

}

Использование 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;
   }

}

ВАЖНО

В то время как синтаксис для символов лучше, для его работы требуется встроенная поддержка браузера. Вы можете написать его с транспилером, но под капотом он будет издеваться над старыми стандартами ES5. Нативная поддержка WeakMaps лучше, и, с другой стороны, эта функция просто играет с GC и с перечислимой опцией свойств объектов. Итак, в конце концов, это ваш выбор.

Ответ 3

cash представляет собой getter/setter, _cash - это свойство 'private'.

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

Посмотрите на эту страницу для четкого примера.

Ответ 4

Вы вызываете рекурсивно своего получателя.

Это следует за возможной альтернативой:

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

    get cash() {
        return this._cash;
    }

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

Другой, использующий 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;
            }
        });
    }
};

Ответ 5

Получить и установить классы ES6 приносит новый синтаксис для getters и seters on свойства объекта. Get и set позволяет нам запускать код при чтении или запись собственности. У ES5 были также геттеры и сеттеры, но не было широко используется из-за более старых браузеров IE. Усилители и сеттеры ES5 не имеют такого синтаксиса, который приносит нам ES6. Поэтому давайте создадим и установите для нашего свойства name.

Источник: Синтаксис класса JavaScript ES6

Пример:

// 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'