Skip to content
Advertisement

RangeError: Maximum call stack size exceeded on constructor

I’m creating a simple class, but I get an error that I can’t understand. My class is very simple:

class Character {
    constructor(firstName, lastName, height){
        this.firstName = firstName;
        this.lastName = lastName;
        this.height = height;
    }

    get firstName(){
        return this.firstName;
    }
    
    set firstName(newName){
        this.firstName = newName;
    }

}

const hombre = new Character("Pedro", "Blanch", 186);

console.log(hombre.firstName);

But when I run in terminal with node script.js I always get an RangeError: Maximum call stack size exceeded error…. Where is the mistake?

Thanks!

Advertisement

Answer

In ECMAScript (which defines JavaScript), when you declare a class method with get and/or set keyword, a what is known as a property descriptor is created, on the prototype object. What you have in your class is absolutely equivalent to the following code:

Object.defineProperty(Character.prototype, "firstName", {
    get() { /* get first name */ }
    set(value) { /* set first name */ }
});

Both accomplish exactly the same thing — accessing firstName on objects of class Character, no matter how or from where, will always invoke the “getter” method. Even in your constructor, this.firstName = firstName; will invoke the “setter” method!

So, when you have an object of class Character, let’s call it good_char for example (var good_char = new Character), and evaluate good_char.firstName or this.firstName in one of the class methods (including any of get or set methods), the get function above will be called. That’s all well and good — that’s what you want, right?

If said function attempts to evaluate this.firstName, that will naturally again cause the same get function to be called — to get the value of the property, after all — a recursive call, without endget is then called again, encounters this.firstName, calls itself, encounters this.firstName… and so on ad infinitum.

That’s why the script interpreter tells you that it has run out of stack space — the stack is what tracks what calls what, and in your case get calls itself recursively, exhausting stack space. The RangeError refers to the stack growing out of allowed range.

All this behaviour basically should tell you that you cannot have a property that seemingly uses another property with the same name — they’re both the same property, accessed only through the getter method and assigned with the setter method. It doesn’t matter whether it’s accessed “from inside” the class method — as this.firstName, or “from outside” — as good_char.firstName — the get will be called to, well, get the value of the property as per the descriptor that was created. You can’t get to any “actual”, “true” value unless you implement it through another property or otherwise, yourself. The descriptor you create with get keyword or Object.defineProperty does not hide any underlying value provided to you that you can access.

This may be confusing to you if you come from another programming language background where “getter” property paradigm is implemented differently.

Practically, if you insist, you will need to encapsulate something like _firstName (doesn’t matter what you call it, it’s just a convention for this kind of programming pattern) behind your actual firstName property that uses get and/or set, yourself. That is a bit of an anti-pattern, however — why do you need getter and setter methods for a property, when all they do is get some value (_firstName) value and set it?

See also Object.defineProperty.

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement