Reputation: 1526
I've reduced the code to a minimum:
var util = require('util');
function ErrorDecorator(error) {
Error.call(this);
this.error = error;
}
util.inherits(ErrorDecorator, Error);
Object.defineProperty(ErrorDecorator.prototype, 'name', {
configurable: false,
enumerable: true,
get: function () {
return this.error.name;
}
});
var e = new ErrorDecorator(new Error());
// TypeError: Cannot read property 'name' of undefined
console.log(e.toString());
I've come across an answer to another question which suggests that I use Object.defineProperty
in the constructor instead of on the prototype. I do not fully understand why, but I can only assume that when I define name
on the prototype that it's called "statically" and not with the instance as its context?
TLDR this code works:
var util = require('util');
function ErrorDecorator(error) {
Error.call(this);
this.error = error;
Object.defineProperty(this, 'name', {
configurable: false,
enumerable: true,
get: function () {
return this.error.name;
}
});
}
util.inherits(ErrorDecorator, Error);
var e = new ErrorDecorator(new Error());
// Error
console.log(e.toString());
Even though I've found a solution I'm still interested in knowing why my first code example doesn't work, and if what I'm doing is against best practices.
Upvotes: 3
Views: 678
Reputation: 106453
Consider the following:
var x = new Error();
var y = Object.create(x);
y.props = {
name: 42
};
var z = Object.create(y);
Object.defineProperty(y, 'name', {
configurable: false,
enumerable: true,
get: function () {
console.log('Is it Z?', this === z);
console.log('Is it Y?', this === y);
return this.props.name;
}
});
console.log(z.toString());
It's tricky: when toString
is called on an object that a) doesn't have its own implementation of toString
and b) has Error
instance in its prototype chain, it follows the algorithm described in the standard:
15.11.4.4 Error.prototype.toString ( )
The following steps are taken:
- Let
O
be thethis
value.- If
Type(O)
is notObject
, throw aTypeError
exception.- Let
name
be the result of calling the[[Get]]
internal method ofO
with argument "name".- If
name
isundefined
, then let name be "Error"; else let name beToString(name)
.
The problem is that for some reason nodejs (0.10.33) seems to make a step up the prototype chain when in that getter: the results are false
and true
accordingly. Both Firefox and Chrome correctly use z
as getter context.
Upvotes: 3