Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extends behaviour in js with default value

I have found interesting behaviour of js extends, and do not understand the reasons of it
in case of copy values right from another value, for some reasons value will be copied from parent

class parent {
  defaultValue = 1;
  value = this.defaultValue;
}
new parent() // {defaultValue: 1, value: 1}
class child extends parent {
  defaultValue = 2;
}
new child() // {defaultValue: 2, value: 1}

which is really not obvious and unclear for me
but if i replace it by function or even getter the behaviour will be changed, and i get the value from child

class parent {
  get defaultValue() { return 1; }
  value = this.defaultValue;
}
new parent() // {defaultValue: 1, value: 1}
class child extends parent {
  get defaultValue() { return 2; }
}
new child() // {defaultValue: 2, value: 2}

the main question here, is why in the moment of child creation in first case JS looking on parent class to take value, but in second case JS looking on child class to take value

Can someone explain the reason of such behaviour?

EDIT See t.niese or Yury Tarabanko answers for details

the short answer seems in next way

getters(also function) and function will be overridden in prototype which allow them to be called by parent with child changes (in real it is expected)

While first example with assignee simple values will be called only in the moment of class creation (constructor or super) and it will be appear only in scope of current class (which cannot be changed by child) and prototype (which can be changed by child)

like image 442
vinger Avatar asked Oct 21 '25 11:10

vinger


2 Answers

A related question is: how to access overridden parent class functions in parent class code.

Getters and Setters are functions that are defined with the definition of the class, so in the constructor of the parent class (and the initiation of its instance class fields) you could call a function that only exists in child (which indeed might be a bit strange):

class parent {
  value = this.test();
  constructor() {
    this.test()
  }
}

class child extends parent {
  test() {
    console.log('test')
  }
}

new child()

So which function (or getter/setter) is called is already defined with the class definition, before the instancing is done.

Public instance class fields on the other hand are initialized/set during initialization phase of an instance in an particular order (the shown code might only work in chrome based browsers):

class parent {
  defaultValue = (() => {
    console.log('parent:init defaultValue')
    return 1;
  })();

  value = (() => {
    console.log('parent:init value')
    return this.defaultValue;
  })();

  constructor() {
    console.log('parent constructor')
  }
}

class child extends parent {
  defaultValue = (() => {
    console.log('child:init defaultValue')
    return 2;
  })();


  constructor() {
    console.log('child constructor before super()')
    super()
    console.log('child constructor after super()')
  }
}

new child()
like image 56
t.niese Avatar answered Oct 23 '25 01:10

t.niese


In your first example, the creation and initialization of the public instance field named defaultValue in Child occurs after the creation and initialization of the public instance field named value in Parent.

So: even though the this value in the initializer of the public instance field named value in Parent will point to the instance of Child under construction, the child-local public instance field named defaultValue does not yet exist, and so the prototype chain is followed up to the property named defaultValue on the instance of Parent, yielding 1.

In your latter example, you have getter functions named defaultValue.

Getter functions specified in this way, even though their API deliberately looks like that of public instance fields, will end-up as functions on the [[Prototype]] of any instance under construction.

The [[Prototype]] objects of instances are created at the time of class declaration (ie. always before anything triggered by instance construction), as the .prototype property of the class (or constructor function); references to these objects are then copied to the [[Prototype]] of any instance under construction as the first step in object construction (think Object.create(class.prototype)).

And so this.defaultValue from the Parent public instance initializer for value resolves to the getter on the [[Prototype]] of the instance of Child under construction, which is a function that returns 2.

like image 44
Ben Aston Avatar answered Oct 22 '25 23:10

Ben Aston



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!