Class Checking: “instanceof”
The operator instanceof is targeted at checking whether an object belongs to a specific class. It also helps to take inheritance into account.
Now, let’s use it to build a polymorphic function, which treats arguments differently depending on their type.
The instanceof operator¶
The following syntax is used for the instanceof operator:
obj instanceof Class
It may return true in case the object belongs to the Class or the class inheriting from it.
Here is an example:
class Dog {}
let dog = new Dog();
// is it an object of Dog class?
console.log(dog instanceof Dog); // true
It can work with constructor functions, as well:
// instead of class
function Dog() {}
console.log(new Dog() instanceof Dog); // true
Also, it works with built-in classes, such as Array:
let arr = [1, 2, 3];
console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true
Please, take into consideration that arr belongs to the Object class. The reason is that Array prototypically inherits from Object.
As a rule, the instanceof studies the prototype chain for the check. It is also possible to apply a custom logic inside the static method Symbol.hasInstance.
The obj instanceof Class algorithm works as follows:
// setup instanceOf check that assumes that
// anything with canEat property is an animal
class Animal {
static[Symbol.hasInstance](obj) {
if (obj.eats) return true;
}
}
let obj = {
eats: true
};
console.log(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) is called
So, if there exists a static method Symbol.hasInstance, it can be called Class[Symbol.hasInstance](obj). It may either return true or false.
But most of the cases don’t include Symbol.hasInstance. In such cases, you need to follow the standard logic: obj instanceOf Class will check whether Class.prototype is the same as one of the prototypes in the obj prototype chain.
Here is an example:
obj.__proto__ === Class.prototype ?
obj.__proto__.__proto__ === Class.prototype ?
obj.__proto__.__proto__.__proto__ === Class.prototype ?
...
// if any answer is true, return true
// otherwise, if we reach the end of the chain, return false
In the example given above dog.__proto__ === Dog.prototype, the answer is given immediately.
If there is an inheritance, the match is at the second step, as follows:
class Animal {}
class Dog extends Animal {}
let dog = new Dog();
console.log(dog instanceof Dog); // true
// dog.__proto__ === Dog.prototype
// dog.__proto__.__proto__ === Animal.prototype
Let’s take a look at the illustrations below:
Another method can be highlighted. It’s objA.isPrototypeOf(objB), which returns true in case objA is inside the chain of prototypes and Class.prototype matters.
It may lead to extraordinary results when a prototype property is transformed after the object is generated.
Here is an example:
function Dog() {}
let dog = new Dog();
// changed the prototype
Dog.prototype = {};
// not a dog any more!
console.log(dog instanceof Dog); // false
Bonus: Object.prototype.toString for the type¶
As it was already noted, plain objects can be converted to a string as [object Object], like here:
let obj = {};
console.log(obj); // [object Object]
console.log(obj.toString()); // the same
It’s their implementation of toString. But, there exists a hidden feature, making toString more robust than that. It may be used as an extended typeof, as well as an alternative for instanceof.
The built-in toString may be extracted from the object, as well as executed in any other value’s context. The result of it depends on that value.
- It can be [object Number] for a number.
- It can be [object Boolean] for a boolean.
- It can be [object Null] for null.
- It can be [object Undefined] for undefined.
- [object Array] : for arrays.
It’s demonstrated in the example below:
// copy toString method into a variable for convenience
let objToString = Object.prototype.toString;
let arr = []; // what type is this?
console.log(objToString.call(arr)); // [object Array]
The toString algorithm explores this returning the corresponding result.
Here is another example:
let str = Object.prototype.toString;
console.log(str.call(123)); // [object Number]
console.log(str.call(null)); // [object Null]
console.log(str.call(alert)); // [object Function]
Symbol.toStringTag¶
The Object toString behavior may be customized, applying a unique object property that is Symbol.toStringTag.
The example is as follows:
let animal = {
[Symbol.toStringTag]: "Animal"
};
console.log({}.toString.call(animal)); // [object Animal]
There is such property for most of the environment-specific objects. For instance:
// toStringTag for the environment-specific object and class:
console.log(window[Symbol.toStringTag]); // window
console.log(XMLHttpRequest.prototype[Symbol.toStringTag]); // XMLHttpRequest
console.log({}.toString.call(window)); // [object Window]
console.log({}.toString.call(new XMLHttpRequest())); // [object XMLHttpRequest]
So, you can notice that the result is Symbol.toStringTag that is wrapped into [object ...].
The {}.toString.call may be used instead of instanceof for built-in objects whenever it’s necessary to receive the type as a string rather than just check.
0 Comments
CAN FEEDBACK
Emoji