1

I have a class that is extended by many other classes.

I pass data to through an instances of these classes and they seem to call a 'this' property of the main class and each of them are able to do it, does this mean they have their own 'copy' of this value?

I really want to understand how this all works, how is data passed through the classes and used specifically on each one, Il show the code:

The parent class (just a relevant section of it):

export abstract class EngagementGraphNode {
  public endpoint: string;
  public id: string;
  public name: string;
  public x?: number;
  public y?: number;
  public parent?: EngagementGraphNode;
  public children: EngagementGraphNode[] = [];
}

Then I have a class which access some of the properties such as this.children:

export class EngagementProduct extends EngagementGraphNode {
  public engagements: Engagement[] = [];
  public description: string;
  public timelineRows: TimelineRow[] = [];

  setProperties(json: IEngagementGraphNode, placeholder: string, color: string) {
}

  setChildren(filters: IFilterParams, rebuild: boolean): void {
    let list = [];
    this.engagements.forEach(engagement => {
      const item = this.find(engagement, filters, rebuild);
      if (this.matchFilters(engagement, filters) && item) { list.push(item) }
    });
      this.children = list;
}

Then there are several other classes which also use 'this.children'

I think that each this.children relates to the single variable and ultimate value that is contained within the parent class, which means that each class must be being called at separate times for this to work? as it does work.

Could someone explain that if possible please?

Also It looks like the classes call on eachother to send data when the app is first loaded so the data is 'parsed' through - is this what is happening? Perhaps I need to read some computer science books and understand the nitty gritty of whats going on with how classes are called, their sequences and how the data is assigned to instances and how that works.

1 Answer 1

3

The property belongs to the objct, not the class. The property is available to code in all the classes because the expression this.children has nothing to do with the classes, it has to do with the object this refers to and whether it has a property called children. It does, so the property accessor works.

Let's simplify a little. Suppose you have:

class A {
    constructor(answer) {
        this.answer = answer;
    }
    aMethod() {
        console.log("aMethod: " + this.answer);
    }
}
class B extends A {
    bMethod() {
        console.log("bMethod: " + this.answer);
    }
}
class C extends B {
    cMethod() {
        console.log("cMethod: " + this.answer);
    }
}
const first  = new C(42);
first.aMethod();  // 42
first.bMethod();  // 42
first.cMethod();  // 42

There is only one object created by new C(42). It has the features (such as methods) of class A, class B, and class C, because it inherits from the prototypes associated with those classes. Once you've created the object (first), what you have in memory (ignoring several details) looks something like this:

           +−−−−−−−−−−−−−−−+
first−−−−−>|  (C Object)   |
           +−−−−−−−−−−−−−−−+       +−−−−−−−−−−−−−−−−−−−−−+
           | [[Prototype]] >−−−−−−>|     C.prototype     |
           | answer: 42    |       +−−−−−−−−−−−−−−−−−−−−−+   +−−−−−−−−−−−−−−−−−−−−−+
           +−−−−−−−−−−−−−−−+       | [[Prototype]]       >−−>|     B.prototype     |
                                   | cMethod: (function) |   +−−−−−−−−−−−−−−−−−−−−−+   +−−−−−−−−−−−−−−−−−−−−−+
                                   +−−−−−−−−−−−−−−−−−−−−−+   | [[Prototype]]       >−−>|     A.prototype     |
                                                             | bMethod: (function) |   +−−−−−−−−−−−−−−−−−−−−−+
                                                             +−−−−−−−−−−−−−−−−−−−−−+   | [[Prototype]]       >−−>(Object.prototype)
                                                                                       | aMethod: (function) |
                                                                                       +−−−−−−−−−−−−−−−−−−−−−+

When you call a method on it, in the normal case this refers to the object you called the method on, which has the property answer on it because the A constructor put it there. It doesn't matter where that method is defined, this still refers to that same single object.

How did the A constructor get called to put answer there? If you don't write a constructor for a subclass, the JavaScript engine creates one for you that looks like this:

constructor(...args) {
    super(...args);
}

B and C both have that implicit constructor. So new C(42) calls C's constructor with 42, which calls B's constructor with 42, which calls A's constructor with 42, which puts it on the object being created via this.answer = answer;.

If you do write a constructor for a subclass, it has to call super (and it has to do it before using this), so one way or another, A will get called.

To see that the property belongs to the object, let's create two objects rather than one:

class A {
    constructor(answer) {
        this.answer = answer;
    }
    aMethod() {
        console.log("aMethod: " + this.answer);
    }
}
class B extends A {
    bMethod() {
        console.log("bMethod: " + this.answer);
    }
}
class C extends B {
    cMethod() {
        console.log("cMethod: " + this.answer);
    }
}
const first  = new C(42);
const second = new C(67);
first.aMethod();  // 42
second.aMethod(); // 67
first.bMethod();  // 42
second.bMethod(); // 67
first.cMethod();  // 42
second.cMethod(); // 67

Now, what's in memory looks like this — the only change is that there's now a second object.

           +−−−−−−−−−−−−−−−+
first−−−−−>|  (C Object)   |
           +−−−−−−−−−−−−−−−+
           | [[Prototype]] >−−−+
           | answer: 42    |   |
           +−−−−−−−−−−−−−−−+   |
                               |   +−−−−−−−−−−−−−−−−−−−−−+
                               +−−>|     C.prototype     |
           +−−−−−−−−−−−−−−−+   |   +−−−−−−−−−−−−−−−−−−−−−+   +−−−−−−−−−−−−−−−−−−−−−+
second−−−−>|  (C Object)   |   |   | [[Prototype]]       >−−>|     B.prototype     |
           +−−−−−−−−−−−−−−−+   |   | cMethod: (function) |   +−−−−−−−−−−−−−−−−−−−−−+   +−−−−−−−−−−−−−−−−−−−−−+
           | [[Prototype]] > −−+   +−−−−−−−−−−−−−−−−−−−−−+   | [[Prototype]]       >−−>|     A.prototype     |
           | answer: 67    |                                 | bMethod: (function) |   +−−−−−−−−−−−−−−−−−−−−−+
           +−−−−−−−−−−−−−−−+                                 +−−−−−−−−−−−−−−−−−−−−−+   | [[Prototype]]       >−−>(Object.prototype)
                                                                                       | aMethod: (function) |
                                                                                       +−−−−−−−−−−−−−−−−−−−−−+
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you T.J, this was extremely helpful. Knowledge is so powerful, the way I am now looking at my code, the classes and how the application runs and uses data is now in a way with more understanding. Is there any more material online that's as clear and informative as this so I can continue to learn?
@Sparlarva - Glad that helped! I can't point to anything specific (I mean, I have a lot of answers here on SO explaining JavaScript concepts, but...). You might check out JavaScript for Impatient Programmers, Axel R. knows his stuff and writes fairly well.
(I should say that I haven't read any of it, just seen his tweets about it; but I've read a few articles on his blog over the years and they've been pretty good.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.