简体   繁体   中英

Too much recursion error when a classfield is not declared

If we declare a class like this:

class client{
  constructor(name){
    this.name = name
  }

  get name()
  {
    return this.name ; 
  }
  set name(val)
  {
    this.name = val ;
  }
}

Instantiate it will give an error ( too much recursion) and that normal because the setter will call itself.

 class client{ constructor(name){ this.name = name } get name() { return this.name; } set name(val) { this.name = val; } } let cl = new client();

But when i declare 'name' as class field i don't get that error, I don't understand why.

 class client{ name = null; constructor(name){ this.name = name } get name() { return this.name; } set name(val) { this.name = val; } } let p = new client(); console.log(p);

TL;DR You can't have both a data property and an accessor property with the same name on the same object, and if you have a data property on an object, any property with the same name on its prototype won't be used (unless you do so explicitly). The name class field definition creates an own data property on the object being constructed.


In the first case (as I think you know), this.name inside the name setter calls the name setter again, recursing until you run out of stack. (And the same for the getter, if you used it.)

So what's different when you have that name field definition there?

The way class fields are defined, that name definition creates an own property on the instance being created, as though you had this code at the beginning of the constructor (just after a super call if this were a subclass):

constructor(name) {
    // Definition of `name` field:
    Object.defineProperty(this, "name", {
        value: undefined,
        writable: true,
        configurable: true,
        enumerable: true,
    });
    // Remainder of constructor code:
    this.name = name;
}

Since it's an own property, this.name resolves to that data property, not to the accessor property on the prototype that the get name and set name accessor definitions create. So those accessors on p 's prototype are never used.

You can see that if you look at the definition of the property in your second example:

 class client { name = null; constructor(name){ this.name = name; } get name() { return this.name; } set name(val) { this.name = val; } } let p = new client(); console.log(p); console.log(Object.getOwnPropertyDescriptor(p, "name"));

If you want to have the accessor property, you could use a private field, which is quite new but supported in modern environments:

 class Client { #name = null; constructor(name) { this.#name = name; } get name() { return this.#name; } set name(val) { this.#name = val; } } let p = new Client("Joe"); console.log(p.name); console.log(Object.getOwnPropertyDescriptor(p, "name")); // undefined console.log(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(p), "name")); // accessor
 .as-console-wrapper { max-height: 100%;important; }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM