Skip to content

Class checking: "instanceof" #398

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Yeah, looks strange indeed.
Sim, parece estranho de fato.

But `instanceof` does not care about the function, but rather about its `prototype`, that it matches against the prototype chain.
Porém, `instanceof` não se importa com a função, mas sim com o seu `prototype`, e assim procura correspondência na cadeia de protótipos.

And here `a.__proto__ == B.prototype`, so `instanceof` returns `true`.
E aqui em `a.__proto__ == B.prototype`, `instanceof` retorna `true`.

So, by the logic of `instanceof`, the `prototype` actually defines the type, not the constructor function.
Então, pela lógica de `instanceof`, o `prototype` na verdade define o tipo, não é a função construtora.
6 changes: 3 additions & 3 deletions 1-js/09-classes/06-instanceof/1-strange-instanceof/task.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
importance: 5
Importance: 5

---

# Strange instanceof
# instanceof estranho

In the code below, why does `instanceof` return `true`? We can easily see that `a` is not created by `B()`.
No código abaixo, por que `instanceof` retorna `true`? Podemos ver facilmente que `a` não é criado por `B()`.

```js run
function A() {}
Expand Down
134 changes: 67 additions & 67 deletions 1-js/09-classes/06-instanceof/article.md
Original file line number Diff line number Diff line change
@@ -1,62 +1,62 @@
# Class checking: "instanceof"
# Verificação de classe: "instanceof"

The `instanceof` operator allows to check whether an object belongs to a certain class. It also takes inheritance into account.
O operador `instanceof` permite checar se um objeto pertence a uma determinada classe. Também leva a herança em consideração.

Such a check may be necessary in many cases. For example, it can be used for building a *polymorphic* function, the one that treats arguments differently depending on their type.
Essa verificação pode ser necessária em diversos casos. Por exemplo, pode ser usada para construir uma função *polimórfica*, aquela que lida com argumentos de forma diferente dependendo do seu tipo.

## The instanceof operator [#ref-instanceof]
## O operador instanceof [#ref-instanceof]

The syntax is:
A sintaxe é:
```js
obj instanceof Class
```

It returns `true` if `obj` belongs to the `Class` or a class inheriting from it.
O código retorna `true` se `obj` pertence à `Class` ou a uma classe herdada dela.

For instance:
Por exemplo:

```js run
class Rabbit {}
let rabbit = new Rabbit();

// is it an object of Rabbit class?
// é um objeto da classe Rabbit?
*!*
alert( rabbit instanceof Rabbit ); // true
*/!*
```

It also works with constructor functions:
Também funciona com funções construtoras:

```js run
*!*
// instead of class
// ao invés de class
function Rabbit() {}
*/!*

alert( new Rabbit() instanceof Rabbit ); // true
```

...And with built-in classes like `Array`:
...E também com classes nativas como `Array`:

```js run
let arr = [1, 2, 3];
alert( arr instanceof Array ); // true
alert( arr instanceof Object ); // true
```

Please note that `arr` also belongs to the `Object` class. That's because `Array` prototypically inherits from `Object`.
Perceba que `arr` também pertence à classe `Object`. Isso porque `Array` de forma prototípica herda de `Object`.

Normally, `instanceof` examines the prototype chain for the check. We can also set a custom logic in the static method `Symbol.hasInstance`.
Normalmente `instanceof` examina a cadeia de protótipos para a verificação. Também podemos definir uma lógica customizada no método estático `Symbol.hasInstance`.

The algorithm of `obj instanceof Class` works roughly as follows:
O algoritmo de `obj instanceof Class` funciona mais ou menos da seguinte forma:

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`.
1. Se houver um método estático `Symbol.hasInstance`, basta executá-lo como: `Class[Symbol.hasInstance](obj)`. Ele deve retornar `true` ou `false`, e é tudo. É assim que podemos customizar o comportamento de `instanceof`.

For example:
Por exemplo:

```js run
// setup instanceOf check that assumes that
// anything with canEat property is an animal
// configura a verificação de instanceOf para assumir que
// qualquer coisa com a propriedade canEat é um animal
class Animal {
static [Symbol.hasInstance](obj) {
if (obj.canEat) return true;
Expand All @@ -65,24 +65,24 @@ The algorithm of `obj instanceof Class` works roughly as follows:

let obj = { canEat: true };

alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) is called
alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) é executado
```

2. Most classes do not have `Symbol.hasInstance`. In that case, the standard logic is used: `obj instanceOf Class` checks whether `Class.prototype` is equal to one of the prototypes in the `obj` prototype chain.
2. A maioria das classes não possui `Symbol.hasInstance`. Nesse caso, a lógica padrão é usada: `obj instanceOf Class` verfica se `Class.prototype` é igual a um dos protótipos na cadeia de protótipos de `obj`.

In other words, compare one after another:
Em outras palavras, compara um após o outro:
```js
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
// se qualquer reposta for verdadeira, retorna true
// do contrário, se chegarmos ao fim da cedeia, retorna false
```

In the example above `rabbit.__proto__ === Rabbit.prototype`, so that gives the answer immediately.
No exemplo acima `rabbit.__proto__ === Rabbit.prototype`, de modo que dá a resposta imediatamente.

In the case of an inheritance, the match will be at the second step:
No caso de uma herança, a correspondência será na segunda etapa:

```js run
class Animal {}
Expand All @@ -93,76 +93,76 @@ The algorithm of `obj instanceof Class` works roughly as follows:
alert(rabbit instanceof Animal); // true
*/!*

// rabbit.__proto__ === Animal.prototype (no match)
// rabbit.__proto__ === Animal.prototype (sem correspondência)
*!*
// rabbit.__proto__.__proto__ === Animal.prototype (match!)
// rabbit.__proto__.__proto__ === Animal.prototype (correspondência!)
*/!*
```

Here's the illustration of what `rabbit instanceof Animal` compares with `Animal.prototype`:
Aqui está a ilustração do que `rabbit instanceof Animal` vai comparar com `Animal.prototype`

![](instanceof.svg)

By the way, there's also a method [objA.isPrototypeOf(objB)](mdn:js/object/isPrototypeOf), that returns `true` if `objA` is somewhere in the chain of prototypes for `objB`. So the test of `obj instanceof Class` can be rephrased as `Class.prototype.isPrototypeOf(obj)`.
A propósito, também existe um método [objA.isPrototypeOf(objB)](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf), que retorna `true` se `objA` está em algum lugar na cadeia de protótipos do `objB`. Então o teste de `obj instanceof Class` pode ser reescrito como `Class.prototype.isPrototypeOf(obj)`.

It's funny, but the `Class` constructor itself does not participate in the check! Only the chain of prototypes and `Class.prototype` matters.
É engraçado, mas o próprio construtor `Class` não participa na verificação! Apenas a cadeia de protótipos e `Class.prototype` importam.

That can lead to interesting consequences when a `prototype` property is changed after the object is created.
Isso pode levar a consequências interessantes quando uma propriedade `prototype` é alterada depois que um objeto é criado.

Like here:
Como nesse exemplo:

```js run
function Rabbit() {}
let rabbit = new Rabbit();

// changed the prototype
// alterou o prototype
Rabbit.prototype = {};

// ...not a rabbit any more!
// ...não é mais um coelho!
*!*
alert( rabbit instanceof Rabbit ); // false
*/!*
```

## Bonus: Object.prototype.toString for the type
## Bonus: Object.prototype.toString para o tipo

We already know that plain objects are converted to string as `[object Object]`:
Já sabemos que objetos simples convertidos para string exibem o texto `[object Object]`:

```js run
let obj = {};

alert(obj); // [object Object]
alert(obj.toString()); // the same
alert(obj.toString()); // o mesmo
```

That's their implementation of `toString`. But there's a hidden feature that makes `toString` actually much more powerful than that. We can use it as an extended `typeof` and an alternative for `instanceof`.
Essa é a implementação deles de `toString`. Porém, há uma característica escondida que torna `toString` realmente muito mais poderoso do que isso. Podemos usá-lo como um `typeof` estendido e uma alternativa para `instanceof`

Sounds strange? Indeed. Let's demystify.
Soa estranho? De fato. Vamos desmistificar.

By [specification](https://tc39.github.io/ecma262/#sec-object.prototype.tostring), the built-in `toString` can be extracted from the object and executed in the context of any other value. And its result depends on that value.
Pela [especificação](https://tc39.github.io/ecma262/#sec-object.prototype.tostring), o `toString` nativo pode ser extraído do objeto e executado no contexto de qualquer outro valor. E o seu resultado depende desse valor.

- For a number, it will be `[object Number]`
- For a boolean, it will be `[object Boolean]`
- For `null`: `[object Null]`
- For `undefined`: `[object Undefined]`
- For arrays: `[object Array]`
- ...etc (customizable).
- Para um número, será `[object Number]`
- Para boleano, será `[object Boolean]`
- Para `null`: `[object Null]`
- Para `undefined`: `[object Undefined]`
- Para arrays: `[object Array]`
- ...etc (customizável).

Let's demonstrate:
Vamos demonstrar:

```js run
// copy toString method into a variable for convenience
// copia o método toString para uma variável por conveniência
let objectToString = Object.prototype.toString;

// what type is this?
// Que tipo é esse?
let arr = [];

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`.
Aqui usamos [call](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Function/call) como descrito no capítulo [](info:call-apply-decorators) para executar a função `objectToString` no contexto `this=arr`.

Internally, the `toString` algorithm examines `this` and returns the corresponding result. More examples:
Internamente, o algoritmo `toString` examina `this` e retorna o resultado correspondente. Mais exemplos:

```js run
let s = Object.prototype.toString;
Expand All @@ -174,9 +174,9 @@ alert( s.call(alert) ); // [object Function]

### Symbol.toStringTag

The behavior of Object `toString` can be customized using a special object property `Symbol.toStringTag`.
O comportamento de Object `toString` pode ser personalizado usando uma propriedade de objeto especial `Symbol.toStringTag`.

For instance:
Por exemplo:

```js run
let user = {
Expand All @@ -186,33 +186,33 @@ let user = {
alert( {}.toString.call(user) ); // [object User]
```

For most environment-specific objects, there is such a property. Here are some browser specific examples:
Para a maioria dos objetos nativos aos diversos ambientes, existe essa propriedade. Aqui estão alguns exemplos específicos do navegador:

```js run
// toStringTag for the environment-specific object and class:
// toStringTag para o objeto e a classe nativos ao ambiente:
alert( window[Symbol.toStringTag]); // Window
alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest

alert( {}.toString.call(window) ); // [object Window]
alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]
```

As you can see, the result is exactly `Symbol.toStringTag` (if exists), wrapped into `[object ...]`.
Como pode ver, o resultado é exatamente `Symbol.toStringTag` (Se existir), dentro de `[object ...]`.

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.
No final, temos "typeof com esteróides" que não funciona apenas para dados primitivos, mas também para objetos nativos e pode até mesmo ser personalizado.

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.
Podemos usar `{}.toString.call` ao invés de `instanceof` para objetos nativos quando queremos obter o tipo como uma string em vez de apenas verificar.

## Summary
## Conclusão

Let's summarize the type-checking methods that we know:
Vamos listar os métodos de checagem de tipos que conhecemos:

| | works for | returns |
|---------------|-------------|---------------|
| `typeof` | primitives | string |
| `{}.toString` | primitives, built-in objects, objects with `Symbol.toStringTag` | string |
| `instanceof` | objects | true/false |
| | funciona para | retorna |
|---------------|-----------------|---------------|
| `typeof` | primitivos | string |
| `{}.toString` | primitivos, objetos nativos, objetos com `Symbol.toStringTag` | string |
| `instanceof` | objetos | true/false |

As we can see, `{}.toString` is technically a "more advanced" `typeof`.
Como podemos ver, `{}.toString` é tecnicamente um `typeof` "mais avançado".

And `instanceof` operator really shines when we are working with a class hierarchy and want to check for the class taking into account inheritance.
E o operador `instanceof` realmente brilha quando estamos trabalhando com uma hierarquia de classe e queremos verificar a classe considerando a herança.