0

I am trying to set a field value using a basic calculation that uses values set in my constructor, though for some reason when i reference some of the values initialised in my constructor, i get TypeError: Cannot read property of undefined.

Though when i reference this without trying to access any of the values set in my constructor (margin or dimension) , i do not get this error and can see the initialised object.

class viz {
  constructor(dimension, margin) {
    this.dimension = dimension;
    this.margin = margin;
  }

  width = this.dimension.width - this.margin.left - this.margin.right;
  height = this.dimension.height - this.margin.top - this.margin.bottom;
}

const margin = { left: 40, right: 40, top: 40, bottom: 20 };
const dimension = { width: 1000, height: 600 };
let visual = new viz(dimension,margin)

KvothesLute
  • 185
  • 2
  • 14
  • 1
    I don't think you have access to `this` in property initialisers. – VLAZ May 24 '20 at 20:05
  • 1
    Object constants make less sense. – JavaScript May 24 '20 at 20:07
  • 1
    @JavaScript There is no such thing as an object constant, unless you're talking about `Object.freeze`. The fact you're passing values stored in a `const` to the constructor does not make the arguments the constructor takes constants. – connexo May 24 '20 at 20:14
  • @VLAZ i do have access to this in property initialisers because when i replace the property values with just a ```this``` rather than ```this.dimension``` or ```this.margin```, an object is returned with all the information created in the constructor. – KvothesLute May 25 '20 at 07:17
  • @JavaScript why is it object constants make no sense ? – KvothesLute May 25 '20 at 07:17

1 Answers1

4

Class fields, de-sugared, always run near the beginning of the constructor, before properties are assigned to this (though class fields are assigned after a super call, if present - and a super call must be done before references to this in the constructor too):

class Parent {
}
class viz extends Parent {
  constructor(dimension, margin) {
    console.log('before super in constructor');
    super();
    console.log('after super');
    this.dimension = dimension;
    this.margin = margin;
  }

  something = console.log('class field');
  width = this.dimension.width - this.margin.left - this.margin.right;
  height = this.dimension.height - this.margin.top - this.margin.bottom;
}

const margin = { left: 40, right: 40, top: 40, bottom: 20 };
const dimension = { width: 1000, height: 600 };
let visual = new viz(dimension,margin)

You can't use a class field if you want to reference properties created in the constructor. You'll have to put that functionality into the constructor instead.

class viz {
  constructor(dimension, margin) {
    this.dimension = dimension;
    this.margin = margin;
    
    
    this.width = this.dimension.width - this.margin.left - this.margin.right;
    this.height = this.dimension.height - this.margin.top - this.margin.bottom;
  }
}

const margin = { left: 40, right: 40, top: 40, bottom: 20 };
const dimension = { width: 1000, height: 600 };
let visual = new viz(dimension,margin)

console.log(visual);
VLAZ
  • 26,331
  • 9
  • 49
  • 67
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • "Class fields, de-sugared, always run near the beginning of the constructor, before properties are assigned to this", if this is the case then why is it when we replace the ```something = console.log('class field')``` with ```something = console.log(this)``` we get an object that shows all the assigned constructor properties ? Surely this suggests they're assigned after the constructor ? – KvothesLute May 25 '20 at 07:15
  • 1
    @KvothesLute Don't let yourself be fooled by `console.log`. – connexo May 25 '20 at 07:29
  • 1
    @KvothesLute [the console is lazy when evaluating objects](https://stackoverflow.com/questions/4057440/is-chromes-javascript-console-lazy-about-evaluating-arrays). So, you *don't* see the state of `this` at the time of the log. The `console.log` prints the reference and at the time you examine it, it's already been populated. – VLAZ May 25 '20 at 14:29