Symbol.hasInstance
Version: ES2015
Level: Advanced
By default, the instanceof
operator checks an object to see if it belongs to a constructor by checking the prototype chain of the object. If the prototype chain has an object matching the prototype
property of the constructor, instanceof
returns true.
let array = new Array();
console.log(array instanceof Array); // true
console.log(array instanceof Object); // true
let object = new Object();
console.log(object instanceof Array); // false
console.log(object instanceof Object); // true
This behavior can be customized using Symbol.hasInstance
. When a constructor implements a static Symbol.hasInstance
, that will be used to determine whether or not an object called with instanceof
is an instance of that constructor overriding the standard prototype check.
class Faker {
static [Symbol.hasInstance] (instance) {
return instance.fakable;
}
}
let normal = {};
let faked = { fakable: true };
console.log(normal instanceof Faker); // false
console.log(faked instanceof Faker); // true
One thing to be careful of is, because the Symbol.hasInstance
method replaces the normal check, it could also return false positives; if an instance was actually created by a certain constructor, instanceof
might not reflect that.
class Faker {
static [Symbol.hasInstance] (instance) {
return instance.fakable;
}
}
let fakedFaker = new Faker();
console.log(fakedFaker instanceof Faker); // false
If you want access to the default behavior, you can call the Symbol.hasInstance
method of Function.prototype
. This way, if you want to fallback to the default behavior when your custom check fails, you have the option to do so.
class Faker {
static [Symbol.hasInstance] (instance) {
let origHas = Function.prototype[Symbol.hasInstance];
return instance.fakable || origHas.call(this, instance);
}
}
let fakedFaker = new Faker();
console.log(fakedFaker instanceof Faker); // true
Symbol.hasInstance
can be useful with mixins. If your mixin is defined by a constructible function, that function can be used with instanceof
to determine whether or not the mixin was applied to an instance used with instanceof
let eaterMixin = Symbol('eaterMixin');
function eater (base) {
return class extends base {
constructor (...args) {
super(...args);
this[eaterMixin] = true;
}
eat () {
console.log('Nom nom');
}
}
}
Object.defineProperty(eater, Symbol.hasInstance, {
value (instance) {
return eaterMixin in instance;
}
});
class Hippo {}
let EatingHippo = eater(Hippo);
let hungryHippo = new Hippo();
let chonkyHippo = new EatingHippo();
chonkyHippo.eat(); // Nom nom
console.log(hungryHippo instanceof Hippo); // true
console.log(hungryHippo instanceof eater); // false
console.log(chonkyHippo instanceof Hippo); // true
console.log(chonkyHippo instanceof eater); // true
Here, the eater
mixin adds a symbol property to each instance it creates which is then used with the Symbol.hasInstance
check. If that symbol doesn’t exist, its not an “instance of” the mixin.
Note that a symbol doesn’t have to be used for this check. You could also do something else to identify the mixin, such as check to see if the instance has the methods/properties defined by the mixin. Symbols are useful in that they’re unique and mostly non-intrusive.
Also worth noting is that to set Symbol.hasInstance
, Object.defineProperty()
was necessary because the default Symbol.hasInstance
in Function.prototype
is defined as not writable. Because of this a normal assignment would not work. Using define on the instance ignores the writable characteristic in the inherited value.
More info: