3

I tried doing some simple type checking for errors, and had the following code:

function isError(x) {
  return Error.isPrototypeOf(x)
}

However, if I call the function with an instance of an error, I get false, like so:

isError(new RangeError) // false

So I fired up node (well, io.js anyway), and did the following:

> Object.getPrototypeOf(Object.getPrototypeOf(new RangeError))
[Error]

In the end, if I do a check with instanceof, it works, like so:

> (new RangeError) instanceof Error
true

So, what exactly is going on here?

Marcus Stade
  • 4,724
  • 3
  • 33
  • 54

2 Answers2

7

isPrototypeOf and getPrototypeOf look at the prototype chain directly, while instanceof looks at the .prototype property of the given constructor function. You need to use:

function isError(x) {
  return Error.prototype.isPrototypeOf(x)
}

Your function tests whether x inherits from the Error constructor function object.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • But it's easier to just use `instanceof`? – Ruan Mendes Jan 31 '15 at 16:23
  • This makes sense, thank you. This lead me to try something else: `Object.getPrototypeOf(Error).isPrototypeOf(new RangeError)` returns false, while `Error.prototype.isPrototypeOf(new RangeError)` returns true – what's going on here? – Marcus Stade Jan 31 '15 at 16:23
  • 2
    `Object.getPrototypeOf(new Error()).isPrototypeOf(new RangeError)` returns true, you have to understand the the difference between the prototype property of constructors, and the prototype of instances, usually the `__proto__` – Ruan Mendes Jan 31 '15 at 16:27
  • 2
    That is, `Object.getPrototypeOf(Error)` is `Function.prototype`, because you're asking for the protoype of a function (constructor) – Ruan Mendes Jan 31 '15 at 16:28
  • Of course, this makes sense. Not enough coffee today I think (or too much!) Thank you very much for the insight! – Marcus Stade Jan 31 '15 at 16:31
  • @al-Zami: First of all, there is no relationship established by doing `Parent.apply(this)`; you'll need to actually [set up the prototype chain](http://stackoverflow.com/a/10898859/1048572). Second, there is no constructor set to anything, `.constructor` is just a property inherited from the prototype object. Lastly, sorry for the confusion; I didn't mean to say that `instanceof` looks at the `.constructor` property of the given instance (which it does not), but that `instanceof` is given a constructor to look at. – Bergi Jan 31 '15 at 17:05
  • @al-Zami See http://stackoverflow.com/questions/4152931/javascript-inheritance-call-super-constructor-or-use-prototype-chain/4389429#4389429 – Ruan Mendes Jan 31 '15 at 21:29
3

instanceof looks at the .prototype property (i.e. not [[Prototype]]/__proto__) of the specified constructor function for you (i.e. it does some additional work).

isPrototypeOf doesn't work as hard and requires you to specify the exact object instance you want to check for on the prototype chain.

Error.isPrototypeOf(r = new RangeError); // false.

false because the function Error does not sit on the prototype chain of r. The prototype chain of r is:

r => RangeError.prototype (an instance of Error) => Error.prototype (an instance of Object))

Nowhere on this chain is the Error constructor function, hence false.

If we modify your example like so:

Error.prototype.isPrototypeOf(r = new RangeError); // true.

This returns true because the .prototype property of the Error function is on the prototype chain of r.

Ben Aston
  • 53,718
  • 65
  • 205
  • 331
  • Is really `RangeError.prototype` an instance of `Error`? – Kuba Wyrostek Jan 31 '15 at 21:34
  • @KubaWyrostek Yes, I believe it is. If you know better, please let me know. – Ben Aston Jan 31 '15 at 21:46
  • 1
    I do not know better. I am just asking. :-) Isn't it that it's `RangeError.prototype = Object.create(Error.prototype)` rather than `RangeError.prototype = new Error()`? – Kuba Wyrostek Jan 31 '15 at 21:58
  • 1
    OK, I hadn't considered that subtle distinction. Is it even possible to find out the answer? What you are highlighting is an implementation-related distinction. – Ben Aston Jan 31 '15 at 22:14
  • 1
    I guess not possible. It really depends on how we define "instance". Nevermind my question, I'm just trying to get some things straight in my mind. :-) – Kuba Wyrostek Jan 31 '15 at 22:29