views:

1211

answers:

3

For some reason it looks like constructor delegation doesn't work in the following snippet:

function NotImplementedError() { Error.apply(this, arguments); }
NotImplementedError.prototype = new Error();
NotImplementedError.prototype.name = 'NotImplementedError';

(function test() {
    function assert(condition, msg) {
        if (!condition) {
            debugger;
            throw new Error('AssertionError: ' + msg);
        }
    }
    function assertEquals(candidate, target, msg) {
        var newMsg = ['candidate: "', candidate.toString(), '"; ',
            'target: "', target.toString(), '"; ',
            msg].join('')
        assert(candidate === target, newMsg);
    }
    var msg = 'too lazy to implement';
    var nie = new NotImplementedError(msg);
    assertEquals(nie.message, msg, 'Message must be present');
    assert(nie instanceof Error, 'Must be Error subclass');
    assert(nie instanceof NotImplementedError, 'Must be NotImplementedError instance');
    // TODO: assertRaises
})();

A run through js.jar fails the test:

$ java -jar js.jar test.js
js: "test.js", line 9: exception from uncaught JavaScript throw: Error: AssertionError: candidate: ""; target: "too lazy to implement"; Message must be present

Any ideas as to why, or if there is a better way to create a new Error subclass? Is there a problem with applying to the native Error constructor that I don't know about?

A: 

The constructor needs to be like a factory method and return what you want. If you need additional methods/properties, you can add them to the object before returning it.

function NotImplementedError(message) { return new Error("Not implemented", message); }

x = new NotImplementedError();

Though I'm not sure why you'd need to do this. Why not just use new Error... ? Custom exceptions don't really add much in JavaScript (or probably any untyped language).

pluckyglen
You have to switch on Error-type-hierarchy or object-value in JavaScript because you can only specify a single catch block. In your solution, (x instanceof NotImplementedError) is false, which isn't acceptable in my case.
cdleary
+5  A: 

Update your code to assign your prototype to the Error.prototype and the instanceof and your asserts work.

function NotImplementedError(message) {
    this.name = "NotImplementedError";
    this.message = (message || "");
}
NotImplementedError.prototype = Error.prototype;

However, I would just throw your own object and just check the name property.

throw {name : "NotImplementedError", message : "too lazy to implement"};
Kevin Hakanson
Your object created with an object literal is not an Error so the Firefox error console (and I would assume the one in other browsers as well) won't know how to deal with it in a useful way if it's not caught. I like your first idea much better.
MatrixFrog
On the other hand, if your code is set up so that you KNOW it will be caught (because you ONLY produce that "error" in one particular function, and that function is ALWAYS wrapped in a try/catch) then maybe this is a pretty good idea.
MatrixFrog
Best answer, but taking `Error.prototype` directly is probably bad form. If you later want to add a `NotImplementedError.prototype.toString` the object now aliases to `Error.prototype.toString` -- better to do `NotImplementedError.prototype = new Error()`.
cdleary
I know I had problem with setting prototype to new Error(), but can't remember for sure. I think that gave incorrect info for either the stack or the linenumber of the error.
Kevin Hakanson
+1  A: 

cdleary: Solution at http://blog.getify.com/2010/02/howto-custom-error-types-in-javascript/