Search code examples
javascriptnode.jsprototypal-inheritance

x.__proto__ == X.prototype is not always same as x instanceof X in javascript?


I know that one should not use __proto__ directly in the code, but just out of curiosity I was playing around with this in node. I was under the impression that if x.__proto__ == X.prototype then it means x instanceof X will yield true until I hit this code with primitive values.

> (2).__proto__ ==  Number.prototype
true
> 2 instanceof Number
false
> (2) instanceof Number
false


> "abc".__proto__ == String.prototype
true
> "abc" instanceof String
false
> ("abc") instanceof String
false

Why is this?


Solution

  • instanceof will always yield false if the left-hand side is not an object (see Step 3 here).

    The reason (2).__proto__ == Number.prototype is true is that when you apply a property accessor to a primitive (in this case, .__proto__), a temporary object with the same underlying primitive value is created and used.

    So the thing you're getting the __proto__ property from isn't the same thing you're using in your instanceof cases, because it isn't a primitive. Your test cases would more accurately be:

    > (2).__proto__ == Number.prototype
    true
    > new Number(2) instanceof Number
    true
    

    I was under the impression that if x.__proto__ == X.prototype then it means x instanceof X will yield true

    That's true as far as it goes (or would be if you used ===, for a fairly obscure reason1), but note that the converse is not true: If x instanceof X is true, that doesn't necessarily mean x.__proto__ == X.prototype will be true, for a couple of reasons:

    1. It could be that you need to use x.__proto__.__proto__, or x.__proto__.__proto__.__proto__, or... :-)

    2. x.__proto__ may well be undefined. For instance, it is if you create x like this: x = Object.create(null). The reason is that the __proto__ property accessor is provided by Object.prototype, but if you create an object that doesn't inherit from Object.prototype, it doesn't have the __proto__ property accessor (on a compliant implementation). This is one reason to use Object.getPrototypeOf(x) instead.


    1 Fairly obscure reason: If x was created via Object.create(null) (or using any prototype that doesn't trace back to Object.prototype) and thus doesn't have the __proto__ property accessor, and X.prototype is null, x.__proto__ == X.prototype would be true even though they could well be completely and utterly unrelated, since x.__proto__ would be undefined and undefined == null is true. ;-)