Cutú Chiqueño Cutú Chiqueño - 11 days ago 6
Javascript Question

How to access js event object in a super type that is called from a subtype?

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?

Answer

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.