Class Checking “instanceof”
JavaScript 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. (运算符instanceof旨在检查对象是否属于特定类。它还有助于将继承考虑在内。)
Now, let’s use it to build a polymorphic function, which treats arguments differently depending on their type. (现在,让我们使用它来构建一个多态函数,该函数根据参数的类型以不同的方式处理参数。)
The instanceof operator
The instanceof operator (Instanceof运算符)
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. (如果对象属于类或从中继承的类,则可能返回true。)
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. (请注意, arr属于Object类。原因是Array通常从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. (通常, instanceof研究用于检查的原型链。也可以在静态方法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 ClassSymbol.hasInstance. It may either return true or false. (因此,如果存在静态方法Symbol.hasInstance ,则可以将其称为Class [Symbol.hasInstance] (obj)。它可以返回true或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__ = = = Class.prototype?)
obj.__proto__.__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. (在上面给出的例子中dog.proto = = = Dog.prototype ,会立即给出答案。)
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. (可以突出显示另一种方法。它是objA.isPrototypeOf (objB) ,如果objA位于原型链中并且Class.prototype很重要,则返回true。)
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
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. (这是他们对toString的实现。但是,存在一个隐藏的功能,使得toString比这更健壮。它可以用作扩展typeof ,也可以用作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. (内置的toString可以从对象中提取,也可以在任何其他值的上下文中执行。其结果取决于该值。)
It can be [object Number] for a number. (-数字可以是[object Number]。)
It can be [object Boolean] for a boolean. (-对于布尔值,它可以是[object Boolean]。)
It can be [object Null] for null. (-对于null ,它可以是[object Null]。)
It can be [object Undefined] for undefined. (-对于undefined ,它可以是[object 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. (ToString算法对此进行探索,返回相应的结果。)
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
Symbol.toStringTag
The Object toString behavior may be customized, applying a unique object property that is Symbol.toStringTag. (可以自定义Object toString行为,应用唯一的对象属性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 …]. (因此,您可以注意到结果是封装在[object…]中的Symbol.toStringTag。)
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.