1

I write different parsers of the HTML context of an event in a webpage.

The basic structure goes like this:

registering the event and link it to a parser function.

elem.addEventListener(
    'click',
    (e) => {
        let parser = new UiResultsItemInfoParser(e);
        eventInfo = parser.evntInfo;
        submitEvnt(eventInfo);
    }
);

UiResultsItemInforParser is a sub-type of a super type called EvntParser and e obviously is the event object.

EvntParser looks like this:

function EvntParser (e) {
    this._evntInfo = {};
    this._evntInfo.time = e.timeStamp.toString();
    this._evntInfo.type = 'event';
}

EvntParser.prototype = {
    set evntInfo(e) {
        this._evntInfo.time = e.timeStamp.toString();
        this._evntInfo.type = 'event';
    },
    get evntInfo() {
        return JSON.stringify(this._evntInfo);
    }
};

UiResultsItemInforParser is derived from EvntParser and looks like this:

function UiResultsItemInfoParser(e) {
    EvntParser.call(this, e);
}

UiResultsItemInfoParser.prototype = new EvntParser();
UiResultsItemInfoParser.prototype.constructor = UiResultsItemInfoParser;

Unfortunately, I get TypeError: e is undefined for the line this._evntInfo.time = e.timeStamp.toString(); in EvntParser when the event is triggered and a new UiResultsItemInfoParser object is created in addEventListener.

Why is that and how can I fix it in a way that EvntParser has access to the event object during object creation?

Cutú Chiqueño
  • 855
  • 1
  • 10
  • 24

1 Answers1

1

This

UiResultsItemInfoParser.prototype = new EvntParser();

is an opt-repeated anti-pattern, and the root of your issue. EvntParser expects an argument and assumes it was given it (this._evntInfo.time = e.timeStamp.toString()), so it fails when you do the above.

Instead:

UiResultsItemInfoParser.prototype = Object.create(EvntParser.prototype);

That creates an object that has EvntParser.prototype as its prototype, but without calling EvntParser. (Then your next line, which is exactly right, fixes constructor on the resulting object.)

Later, you're doing the right thing by doing EvntParser.call(this, e); in your subclass constructor. So it's really just the one line.


Since you're using arrow functions, you must be working in an ES2015 environment. That means you can use the new class syntax, which does all this fiddly hooking things up for you:

class EvntParser {
    constructor(e) {
        this._evntInfo = {};
        this._evntInfo.time = e.timeStamp.toString();
        this._evntInfo.type = 'event';
    }
    set evntInfo(e) {
        this._evntInfo.time = e.timeStamp.toString();
        this._evntInfo.type = 'event';
    }
    get evntInfo() {
        return JSON.stringify(this._evntInfo);
    }
}

class UiResultsItemInfoParser extends EvntParser {
}

Note how you don't even need to define a constructor in UiResultsItemInfoParser; when you don't, the JavaScript engine defines one for you that calls super with all the arguments it receives.


Side note: This:

this._evntInfo = {};
this._evntInfo.time = e.timeStamp.toString();
this._evntInfo.type = 'event';

can also be written

this._evntInfo = {
    time: e.timeStamp.toString(),
    type: 'event'
};

if you like (even before ES2015). Exactly the same object is created.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thanks! Really interesting though, that I got the pattern from a brand new reference and beginners book (https://www.amazon.de/JavaScript-Fortgeschrittene-Objektorientierung-funktionale-Programmierung/dp/3836238381). That is really a shame. (Side note: I used pseudo-class notation just to get a feeling for the prototype logic which I do not know so much, but thx anyway!) – Cutú Chiqueño Nov 28 '16 at 17:10
  • 1
    @CutúChiqueño: Oh, that **is** a shame. If the book has a website, maybe you could report a bug... Well, as I say, everything else was right. :-) – T.J. Crowder Nov 28 '16 at 17:11