simba
simba

Reputation: 35

Why is assigning a value to super in the constructor in the ES6 standard? It is assigning a value to an instance of the class

class A {
  constructor() {
    this.x = 1;
  }
}
class B extends A {
  constructor() {
    super();
    this.x = 2;
    super.x = 3;
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
}
let b = new B();

Why is assigning a value to super in the constructor in the ES6 standard? It is assigning a value to an instance of the class. Why is super.x undefined while this.x is 3

Upvotes: -1

Views: 75

Answers (1)

Alexander Nenashev
Alexander Nenashev

Reputation: 23309

What happens

  1. All classes in an instance share the same this (which is an instance of the class used with new), so assigning this.x = 1 in a super's constructor and in the child constructor are the same.
  2. It's kinda interesting what happens when you assign super.x = 3. a x's SETTER is looked up in the prototype chain starting from super (A.prototype) and when not found, x is assigned to this. But when reading super.x, a x's GETTER is looked up from super and when no found there's no fallback for this so undefined is returned 🤷‍♂️.

How to solve (to have (kind of?) multiple same name property per a class in an instance (if needed))

  1. To have a prop per a class you should use private props, they are like symbols (names are just tags in code)
  2. When you add a getter/setter in super for x you should DEFINE the B class with x property, otherwise this.x = 2 in the B's constructor will use the setter and won't assign x to this but rather to this.#x in the super.

class A {
  #x;
  constructor() {
    this.#x = 1;
  }
  get x(){ return this.#x }
  set x(v){ this.#x = v }
}
class B extends A {
  x; // this is required to ensure this.x = 2 to work
  constructor() {
    super();
    this.x = 2;
    super.x = 3;
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
}
let b = new B();

You can also use symbols:

const propX = Symbol('propX');
class A {
  constructor() {
    this[propX] = 1;
  }
  get x(){ return this[propX] }
  set x(v){ this[propX] = v }
}
class B extends A {
  x; // this is required to ensure this.x = 2 to work
  constructor() {
    super();
    this.x = 2;
    super.x = 3;
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
}
let b = new B();

With symbols you could create some generic solution:

class A {}
createClassProp(A, 'x', 1);

class B extends A {
  x; // this is required to ensure this.x = 2 to work
  constructor() {
    super();
    this.x = 2;
    super.x = 3;
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
}
let b = new B();
console.log(new A().x);
<script>
function createClassProp(target, name, initVal){
  const symbol = Symbol(name);
  Object.defineProperty(target.prototype, name, {
    enumerable: false,
    get(){
      if(Object.hasOwn(this, symbol)) return this[symbol];
      return Object.hasOwn(this, name) ? this[name] : initVal;
    },
    set(v){
      this[symbol] = v;
    }
  });
}
</script>

Upvotes: 1

Related Questions