This example demonstrates that Baz.prototype, Bar.prototype, Foo.prototype and Object.prototype exist in the prototype chain for object baz:
class Foo {}
class Bar extends Foo {}
class Baz extends Bar {}
const foo = new Foo();
const bar = new Bar();
const baz = new Baz();
console.log(Baz.prototype.isPrototypeOf(baz));
console.log(Baz.prototype.isPrototypeOf(bar));
console.log(Baz.prototype.isPrototypeOf(foo));
console.log(Bar.prototype.isPrototypeOf(baz));
console.log(Bar.prototype.isPrototypeOf(foo));
console.log(Foo.prototype.isPrototypeOf(baz));
console.log(Foo.prototype.isPrototypeOf(bar));
console.log(Object.prototype.isPrototypeOf(baz));
The isPrototypeOf() method — along with the instanceof operator — comes in particularly handy if you have code that can only function when dealing with objects descended from a specific prototype chain; e.g., to guarantee that certain methods or properties will be present on that object.
For example, to execute some code that's only safe to run if a baz object has Foo.prototype in its prototype chain, you can do this:
if (Foo.prototype.isPrototypeOf(baz)) {
}
However, Foo.prototype existing in baz's prototype chain doesn't imply baz was created using Foo as its constructor. For example, baz could be directly assigned with Foo.prototype as its prototype. In this case, if your code reads private fields of Foo from baz, it would still fail:
class Foo {
#value = "foo";
static getValue(x) {
return x.#value;
}
}
const baz = { __proto__: Foo.prototype };
if (Foo.prototype.isPrototypeOf(baz)) {
console.log(Foo.getValue(baz));
}
The same applies to instanceof. If you need to read private fields in a secure way, offer a branded check method using in instead.
class Foo {
#value = "foo";
static getValue(x) {
return x.#value;
}
static isFoo(x) {
return #value in x;
}
}
const baz = { __proto__: Foo.prototype };
if (Foo.isFoo(baz)) {
console.log(Foo.getValue(baz));
}