greengold
greengold

Reputation: 1333

NodeJS object inheritance

I have an custom error object that is extending the Error object:

function GTError(reason, loglevel, meta) {
    winston.log(loglevel, reason, meta);
    GTError.super_.apply(this, arguments);
}

util.inherits(GTError, Error);

I know that this doesn't work, and this question has workarounds for that. My question isn't how to work around it, my question is why doesn't it work? Why does calling GTError.super_.apply(this, arguments); (where GTError.super_ is Error) not populate the message property of the this?

enter image description here

can you explain, please? Thanks

Upvotes: 1

Views: 127

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074038

My question isn't how to work around it, my question is why doesn't it work?

Because the Error function always returns a new instance, even if you don't call it with new; it doesn't modify the object this refers to when you call it. From the ES5 specification:

When Error is called as a function rather than as a constructor, it creates and initialises a new Error object. Thus the function call Error(…) is equivalent to the object creation expression new Error(…) with the same arguments.

So in your code, GTError.super_.apply(this, arguments); (where GTError.super_ is Error) is effectively a no-op, because it creates and throws away an Error object rather than making Error populate the object passed as this. In fact, there's no way (in ES5) to make Error populate the object created by new in your new GTError expression, because of the way Error was defined.

The TC-39 committee fixed that in ES2015, but only when you're using the new class stuff (in order to maintain backward compatibility).


I know you said you just wanted to know why, but for others: In ES2015 and later, you can correctly subclass Error via the new class stuff. This syntax is supported in at least Node v4 in strict mode, and universally in Node v6. The following will work in any recent version of Chrome or Firefox:

let winston = {
  log: function(...args) {
    console.log("log", ...args);
  }
};
class GTError extends Error {
    constructor(reason, loglevel, meta) {
        winston.log(loglevel, reason, meta);
        super(reason, loglevel, meta);
    }
}
try {
  throw new GTError("reason", 10, "meta");
}
catch (e) {
  console.log(e instanceof Error);
  console.log(e instanceof GTError);
  console.log(e.message);
}

Upvotes: 1

Related Questions