[There are other similar questions on this topic, but none of them answer the question I'm asking here, AFAICT. (I.e. the answers I have read all explain why a particular construct fails to do with the questioner is trying to do, and in some cases they offer alternative ways to get the desired results. But no thread answers the question of how to achieve true inheritance from Error.prototype
.)]
The best I've managed to do is still basically useless:
function MyErr(message) {
var tmp = Error.apply(this, arguments);
for (var prop in tmp) { this[prop] = tmp[prop]; }
}
MyErr.prototype = Object.create(Error.prototype);
Even after "manually" copying properties (a sure sign of a "busted inheritance"), the object returned by new MyErr("whatever")
is still not even remotely close to what I would consider "an instance that inherits from Error.prototype
". The deviations from expected behavior are really too many to list here—they pop up everywhere I look!—, but, for starters,
console.log((new Error("some error")).toString()) // "Error: some error"
console.log((new MyErr("another error")).toString()) // "Error"
// expected
// "MyError: another error"
console.log((new Error("some error")).constructor) // "Error()"
console.log((new MyErr("another error")).constructor) // "Error()"
// expected:
// "MyError()"
(In case anyone is wondering, no, constructor
is not one of the properties that gets copied in MyErr
's for
loop. I checked.)
Can an object truly inherit from Error.prototype
?
Given that to answer the analogous question for Array
requires a lengthy treatise, I can't reasonably expect a full answer to my question here, but I hope I can get a pointer to such full answer.
UPDATE: I tried out Bergi's proposal. Below I provide a full, self-contained implementation.1
<!DOCTYPE html>
<html><head><meta charset="utf-8"></head><body>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
jQuery(document).ready(function ($) {
function MyErr(message) {
var tmp = Error.apply(this, arguments);
Object.getOwnPropertyNames(tmp).forEach(function(p) {
console.log('property: ' + p);
Object.defineProperty(this, p,
Object.getOwnPropertyDescriptor(tmp, p));
}, this);
console.log('done creating ' + this);
}
MyErr.prototype = Object.create(Error.prototype, {
constructor: {value:MyErr, configurable:true},
name: {value:"MyErr", configurable:true}
});
var error = new Error("some error");
var myerr = new MyErr("another error");
console.log(error.toString());
console.log(myerr.toString());
console.log(error.constructor);
console.log(myerr.constructor);
});
</script></body></html>
The output I get in the Firebug console is this:
done creating MyErr testjs.html (line 13)
Error: some error testjs.html (line 23)
MyErr testjs.html (line 24)
Error() testjs.html (line 26)
MyErr(message) testjs.html (line 27)
Note in particular that the output does not include any lines beginning with property:
. This means that the console.log
statement within the forEach
loop in MyErr
never gets executed (and presumably the same goes for the rest of the forEach
callback).
If I replace the forEach
loop with
for (var p in tmp) {
console.log('property: ' + p);
Object.defineProperty(this, p,
Object.getOwnPropertyDescriptor(tmp, p));
};
...then three new lines get prepended to the output (line numbers omitted):
property: fileName
property: lineNumber
property: columnNumber
This shows that not even message
is among the tmp
properties accessible this way (though message
does show up among tmp
's properties in Firebug's variable inspector, along with a bazillion of other ones that are also not shown in the console output above).
Also, the explicitly-set name
property does make an appearance in the output of myerr.toString()
, but still this method does not behave in the same way as error.toString()
.
I do get the expected output from myerr.toString()
if I add the following line at the end of the MyErr
function:
this.message = message;
...but I get little comfort from this, because this maneuver, along with most of the other maneuvers I've shown above, have been ad hoc kluges aimed at the specific examples that I posted, but these were intended only as an illustration of what appears to be a deeper problem, namely a complete breakdown of the expected inheritance model.
The purpose of this question is to find out how to achieve "true inheritance" from Error.prototype
, and if that is not possible, then to simulate it as closely as possible, having a clear picture of the extent to which the simulation fails to implement the full inheritance model. I stress that the latter of these two alternatives should not be confused with the strategy of incrementally patching some flawed implementation every time we happen to notice some new way in which it fails to conform to expectation. This "incremental patching" approach provides the ideal environment for silent bugs, and is thus a recipe for insanity.
UPDATE2: There seems to be no end to JS's unpredictability. I just discovered that, at least in the Firebug console, Object.getOwnPropertyNames
produces a different value depending on whether its argument is a non-atomic expression or a variable to which the same non-atomic expression has been previously assigned. wtf?
For example, the following is copy-pasted directly from an interaction in the Firebug console (I've added one empty line before each command, for ease of reading):
>>> Object.getOwnPropertyNames(new Error("not assigned"))
[]
>>> errorvar = new Error("assigned")
Error: assigned
(no source for debugger eval code)
>>> Object.getOwnPropertyNames(errorvar)
["fileName", "lineNumber", "message", "stack"]
(The output right after the assignment to errorvar
seems to have to do with the fact that an Error
object was created, even though it was not throw
n. The same output appears even if one deletes everything to the left of new
in that line.)
And if that were not enough, if I run this from within a script
console.log(Object.getOwnPropertyNames(new Error("not assigned")))
var errorvar = new Error("assigned");
console.log(Object.getOwnPropertyNames(errorvar));
the output in the Firebug console is
[ ]
[ ]
I don't know if this erratic behavior is due to ECMAScript5, or JavaScript, or Firefox, or Firebug, or what, but it's driving me insane...
1 I posted this self-contained implementation to encourage others to try it out. If there is one thing I have learned about JavaScript is that, no matter how simple the code, and no matter how much you think you know about JavaScript, the only way to know what some JavaScript code is going to do is to run it. Sadly, JavaScript programming still remains an embarrassingly experimental activity. JS is not for the armchair programmer! :)