Check if a constructor inherits another in ES6

2019-01-07 00:13发布

问题:

I have a situation where I need to check if a constructor (X) has another constructor (Y) in its prototype chain (or is Y itself).

The quickest means to do this might be (new X()) instanceof Y. That isn't an option in this case because the constructors in question may throw if instantiated without valid arguments.

The next approach I've considered is this:

const doesInherit = (A, B) => {
  while (A) {
    if (A === B) return true;
    A = Object.getPrototypeOf(A);
  }

  return false;
}

That works, but I can't shake the sense that I'm missing some more straightforward way to check this. Is there one?

回答1:

Because of the way instanceof works, you should be able to do

A.prototype instanceof B

But this would only test inheritance, you should have to compare A === B to test for self-reference:

A === B || A.prototype instanceof B

Babel example:

class A {}
class B extends A {}
class C extends B {}

console.log(C === C) // true
console.log(C.prototype instanceof B) // true
console.log(C.prototype instanceof A) // true

instanceof is basically implemented as follows:

function instanceof(obj, Constr) {
  var proto;
  while ((proto = Object.getProtoypeOf(obj)) {
    if (proto === Constr.prototype) {
      return true;
    }
  }
  return false;
}

It iterates over the prototype chain of the object and checks whether any of the prototypes equals the constructors prototype property.

So almost like what you were doing, but internally.



回答2:

There's also Object.prototype.isPrototypeOf(). Seems like a perfect use case, no?

Babel

class A {}
class B extends A {}
class C extends B {}
console.log(C === C)
console.log(B.isPrototypeOf(C))
console.log(A.isPrototypeOf(C))


回答3:

BEWARE: The above answer of checking ES6 classes A, B, C inheritance-chain with isPrototypeOf() works great. But it doesn't work like you might expect for pre-ES6 classes Object, Array, Function etc.

 Object.prototype.isPrototypeOf (Array.prototype); // => true 

But:

 Object.isPrototypeOf (Array);   // => false

This is like it should be. INSTANCES of Array inherit the methods of Object.prototype. But the "class" Array does NOT inherit the "class" Object. If you add a method to Object you can not call it via Array. Array does not inherit methods of Object (if any). It's just that user-created classes work differently!

Perhaps we should not think of Object and Array and Function as "classes" at all - even though you can create instances of them. They are only "constructors". Or we can say that they are classes but that built-in classes in JavaScript work differently from user-created ones.