fixes
This commit is contained in:
parent
f96872425d
commit
6d1fa5de73
8 changed files with 84 additions and 79 deletions
|
@ -11,7 +11,7 @@ The syntax is:
|
|||
obj instanceof Class
|
||||
```
|
||||
|
||||
It returns `true` if `obj` belongs to the `Class` (or a class inheriting from it).
|
||||
It returns `true` if `obj` belongs to the `Class` or a class inheriting from it.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -46,15 +46,17 @@ alert( arr instanceof Object ); // true
|
|||
|
||||
Please note that `arr` also belongs to the `Object` class. That's because `Array` prototypally inherits from `Object`.
|
||||
|
||||
The `instanceof` operator examines the prototype chain for the check, but we can set a custom logic in the static method `Symbol.hasInstance`.
|
||||
Normally, `instanceof` operator examines the prototype chain for the check. We can also set a custom logic in the static method `Symbol.hasInstance`.
|
||||
|
||||
The algorithm of `obj instanceof Class` works roughly as follows:
|
||||
|
||||
1. If there's a static method `Symbol.hasInstance`, then just call it: `Class[Symbol.hasInstance](obj)`. It should return either `true` or `false`. We're done.
|
||||
For example:
|
||||
1. If there's a static method `Symbol.hasInstance`, then just call it: `Class[Symbol.hasInstance](obj)`. It should return either `true` or `false`, and we're done. That's how we can customize the behavior of `instanceof`.
|
||||
|
||||
For example:
|
||||
|
||||
```js run
|
||||
// setup instanceOf check that assumes that anything that canEat is an animal
|
||||
// setup instanceOf check that assumes that
|
||||
// anything with canEat property is an animal
|
||||
class Animal {
|
||||
static [Symbol.hasInstance](obj) {
|
||||
if (obj.canEat) return true;
|
||||
|
@ -68,17 +70,19 @@ The algorithm of `obj instanceof Class` works roughly as follows:
|
|||
|
||||
2. Most classes do not have `Symbol.hasInstance`. In that case, the standard logic is used: `obj instanceOf Class` checks whether `Class.prototype` equals to one of prototypes in the `obj` prototype chain.
|
||||
|
||||
In other words, compare:
|
||||
In other words, compare one after another:
|
||||
```js
|
||||
obj.__proto__ === Class.prototype
|
||||
obj.__proto__.__proto__ === Class.prototype
|
||||
obj.__proto__.__proto__.__proto__ === Class.prototype
|
||||
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 reached the end of the chain, return false
|
||||
```
|
||||
|
||||
In the example above `Rabbit.prototype === rabbit.__proto__`, so that gives the answer immediately.
|
||||
In the example above `rabbit.__proto__ === Rabbit.prototype`, so that gives the answer immediately.
|
||||
|
||||
In the case of an inheritance, `rabbit` is an instance of the parent class as well:
|
||||
In the case of an inheritance, the match will be at the second step:
|
||||
|
||||
```js run
|
||||
class Animal {}
|
||||
|
@ -88,8 +92,11 @@ The algorithm of `obj instanceof Class` works roughly as follows:
|
|||
*!*
|
||||
alert(rabbit instanceof Animal); // true
|
||||
*/!*
|
||||
|
||||
// rabbit.__proto__ === Rabbit.prototype
|
||||
*!*
|
||||
// rabbit.__proto__.__proto__ === Animal.prototype (match!)
|
||||
*/!*
|
||||
```
|
||||
|
||||
Here's the illustration of what `rabbit instanceof Animal` compares with `Animal.prototype`:
|
||||
|
@ -100,7 +107,7 @@ By the way, there's also a method [objA.isPrototypeOf(objB)](mdn:js/object/isPro
|
|||
|
||||
That's funny, but the `Class` constructor itself does not participate in the check! Only the chain of prototypes and `Class.prototype` matters.
|
||||
|
||||
That can lead to interesting consequences when `prototype` is changed.
|
||||
That can lead to interesting consequences when `prototype` property is changed after the object is created.
|
||||
|
||||
Like here:
|
||||
|
||||
|
@ -117,8 +124,6 @@ alert( rabbit instanceof Rabbit ); // false
|
|||
*/!*
|
||||
```
|
||||
|
||||
That's one of the reasons to avoid changing `prototype`. Just to keep safe.
|
||||
|
||||
## Bonus: Object.prototype.toString for the type
|
||||
|
||||
We already know that plain objects are converted to string as `[object Object]`:
|
||||
|
@ -152,7 +157,7 @@ let objectToString = Object.prototype.toString;
|
|||
// what type is this?
|
||||
let arr = [];
|
||||
|
||||
alert( objectToString.call(arr) ); // [object Array]
|
||||
alert( objectToString.call(arr) ); // [object *!*Array*/!*]
|
||||
```
|
||||
|
||||
Here we used [call](mdn:js/function/call) as described in the chapter [](info:call-apply-decorators) to execute the function `objectToString` in the context `this=arr`.
|
||||
|
@ -196,11 +201,11 @@ As you can see, the result is exactly `Symbol.toStringTag` (if exists), wrapped
|
|||
|
||||
At the end we have "typeof on steroids" that not only works for primitive data types, but also for built-in objects and even can be customized.
|
||||
|
||||
It can be used instead of `instanceof` for built-in objects when we want to get the type as a string rather than just to check.
|
||||
We can use `{}.toString.call` instead of `instanceof` for built-in objects when we want to get the type as a string rather than just to check.
|
||||
|
||||
## Summary
|
||||
|
||||
Let's recap the type-checking methods that we know:
|
||||
Let's summarize the type-checking methods that we know:
|
||||
|
||||
| | works for | returns |
|
||||
|---------------|-------------|---------------|
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue