I am currently experimenting with ECMA6 classes. My current class looks like the following
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
}
};
When I am now creating a new Object by calling let playerObject = new Player(1);
I receive the following error
...\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
What does this have to do with the mysql library? Why does the error is multiple times in the same line? I am only calling it once.
The call stack is limited in size, and when it's exceeded, the RangeError is thrown. This can happen when a deeply nested function is called, or when a lot of new variables are created. The most common way to fix this error is to reduce the number of function calls, or to limit the number of variables that are created.
The consequences of applying a function with too many arguments (think more than tens of thousands of arguments) vary across engines (JavaScriptCore has hard-coded argument limit of 65536), because the limit (indeed even the nature of any excessively-large-stack behavior) is unspecified.
The "RangeError: Maximum call stack size exceeded" error occurs when a function is called so many times that the invocations exceed the call stack limit. To solve the error, specify a base case that has to be met to exit the recursion.
Introduction. If you see the “Maximum Call Stack Size Exceeded” error, there's likely a problem with a recursive function within your JavaScript code. More specifically, the issue lies with the function calling on itself indefinitely.
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.
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.
const CASH = Symbol();
class Player {
constructor () {
this[CASH] = 350;
}
get cash(){
return this[CASH];
}
set cash(cash) {
this[CASH] = cash;
}
}
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;
}
}
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.
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.
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;
}
});
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With