Классы 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? Почему ошибка несколько раз в одной строке? Я звоню только один раз.
5 ответов
Ваш установщик "cash" вызывает установщик "cash", который вызывает установщик "cash", который вызывает установщик "cash"...
Доступ к установщику свойств по его собственному имени внутри установщика создает бесконечный рекурсивный вызов функции.
Я знаю, что опоздал, но я думаю, что могу уточнить один или два момента здесь:
Во-первых, это вопрос конфиденциальности, который является долгосрочным обсуждением в сообществе 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 и с перечисляемой опцией свойств объектов. Итак, в конце концов, это ваш выбор.
cash представляет собой метод получения / установки, _cash - свойство private.
set cash(value) { // line 19
this._cash = value; // line 20
}
Посмотрите на эту страницу для наглядного примера.
Вы рекурсивно звоните своему получателю.
Следует возможная альтернатива:
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;
}
});
}
};
Классы Get & Set ES6 предоставляют новый синтаксис для методов получения и установки свойств объекта. Get and set позволяет нам запускать код для чтения или записи свойства. ES5 также имел геттеры и сеттеры, но из-за старых браузеров IE его не использовали широко. Получатели и установщики ES5 не имели такого приятного синтаксиса, как ES6. Итак, давайте создадим get и set для нашего свойства name.
Источник: JavaScript ES6 Class Синтаксис
Пример:
// 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'