Derek 朕會功夫
Derek 朕會功夫

Reputation: 94319

Check if "instanceof" is going to fail

function A(){}
A.prototype = "Foo bar";

new A() instanceof A;
// TypeError: Function has non-object prototype 'Foo bar' in instanceof check

As you can see, if the prototype of a constructor is not an object, it will fail and throw an error. Is there a way to make sure instanceof will not fail?

typeof new A().constructor.prototype === "object"

and

typeof Object.getPrototypeOf(new A()) === "object"

apparently do not work.

Upvotes: 3

Views: 5613

Answers (4)

Mick Owen
Mick Owen

Reputation: 689

I use: typeof o.prototype === "function" && o instanceof f

Upvotes: 1

RobG
RobG

Reputation: 147363

Perhaps not really an answer, but an interesting conclusion.

When a function is called as a constructor, if its prototype property isn't an Object, then the new instance is assigned the intrinsicDefaultProto as its [[Prototpye]] as described in GetPrototypeFromConstructor.

In the process of creating a new instance, the intrinsicDefaultProto is passed the value of the fallbackProto, which seems to be (in Firefox at least), Object.prototype.

And since the constructor property of Object.prototype is Object, then testing whether the instance's constructor property references the candidate object won't work either.

function Foo(){}
Foo.prototype = 'A';
var foo = new Foo();

// foo inherits directly from Object.prototype, not Foo
console.log(Object.getPrototypeOf(foo) == Object.prototype) // true

// foo also inherits constructor property from Object.prototype
console.log(foo.constructor == Object)  // true

So a circumstance in which instanceof fails likely points to more serious issues in the program design or implementation. Hiding such errors seems to not be a good idea (unless the intention is to leave traps within the code for future maintainers to discover).

Upvotes: 1

Barmar
Barmar

Reputation: 780842

Use try/catch to catch the error;

function isItA(obj) {
  try {
    return obj instanceof A;
  } catch (e) {
    return false; //
  }
}

function A() {}
A.prototype = "Foo bar";
function B() {}
B.prototype = "Baz Quux";
console.log(isItA(new A()));
console.log(isItA(new B()));

Upvotes: 1

Bergi
Bergi

Reputation: 664366

The error says A.prototype needs to be an object, so you should check for that:

function isObject(x) {
    return x != null && (typeof x == "object" || typeof x == "function");
}

but isObject(A.prototype) is not all you can do to assert an instanceof call won't throw. Going by the spec, you should test

function allowsInstanceCheck(C) {
    try {
        if (!isObject(C)) return false;
        var m = C[Symbol.hasInstance];
        if (m != null) return typeof m == "function";
        if (typeof C != "function") return false;
        return isObject(C.prototype);
    } catch (e) {
        // any of the property accesses threw
        return false;
    }
}

Upvotes: 3

Related Questions