Front End

14 dez, 2017

Protótipos no JavaScript – Parte 01: O que são protótipos?

Publicidade

Herança. Você com certeza já deve ter ouvido falar desta palavrinha que aparece em praticamente todos os livros e todas as aulas de programação orientada a objetos.

Em OOP, herança refere-se a habilidade de um objeto acessar métodos e outras propriedades de outro objeto. Estes objetos então herdam essas informações de outros objetos e podem utilizá-las da forma que preferirem. No JavaScript não temos uma herança propriamente dita, mas ela funciona através do que são chamados de protótipos. Esta forma de herança é conhecida como herança prototípica (ou _prototypal inheritance_).

Vamos conhecer um pouco mais sobre isso.

Global Functions

O JavaScript – por padrão – te dá acesso a três tipos de funções globais: Object , Array e Function. Sim, todas são funções. Você achou que Object fosse o que?

console.log(Object); //ƒ Object() { [native code] }
console.log(Array); //ƒ Array() { [native code] }
console.log(Function); //ƒ Function() { [native code] }

Acho que você não sabe disso, mas sempre que você cria o que chamamos de objeto literal no JavaScript (sabe, quando você usa {} como em var obj = {}?) isso é, na verdade, uma chamada implícita para new Object(). Da mesma forma que você pode utilizar a sintaxe explícita para criar um objeto.

O mesmo vale para arrays e funções. Vamos pensar que esses três objetos tem um construtor cada um:

  • Object vem do construtor Object()
  • Array vem do construtor Array()
  • Function vem do construtor Function()

Protótipos de objetos

Todos os objetos em JS possuem o que é chamado de protótipo, e é aqui que as coisas ficam muito interessantes.

__proto__

Como já dissemos antes, todo o objeto possui um protótipo próprio. Essa implementação do protótipo em si é feita pelos browsers e outros runtimes através da propriedade __proto__ (geralmente a comunidade chama isto de _dunder proto_, você sabe, por causa das duplas underscores e etc. E daqui para frente, vamos chamá-los apenas disso.

Esta propriedade nunca foi originalmente incluída nas especificações originais do ECMAScript, embora os navegadores tenham resolvido implementar ela da mesma maneira para garantir a continuidade e compatibilidade com versões futuras. Só agora temos uma inclusão padronizada pelo ES2015.

O __proto__ aparece em vários lugares e é bastante explícito, provavelmente você já viu alguma coisa assim no seu console:

Console mostrando a implementação de um __proto__

A questão é: toda e qualquer coisa que você já usou ou vai utilizar no JS provavelmente vai ter uma propriedade destas aparecendo. E o que podemos fazer com ela?

__NADA!__ Jamais, em nenhuma hipótese use esta propriedade diretamente ou associe ela a outro valor. Se você entrar na página do MDN para o __proto__, já vai poder ver em um bloco grande, vermelho com letras garrafais. Estes mesmos dizeres, muito embora eles digam isso para informar que pode ser um problema de performance.

prototype

Funções também tem uma propriedade chamada prototype. Essa propriedade é diferente de __proto__ . O que torna nossa discussão sobre eles um pouco mais confusa do que o normal. Principalmente para escrever este artigo.

__proto__ e prototype são coisas diferentes

Mas vamos nos dirigir a cada uma pelos próprios nomes, espero que vocês consigam entender o que eu estou dizendo. Se printarmos em um console o resultado de uma função simples (poderia ser um objeto, ou um array), depois o resultado de seu prototype e também o seu __proto__, isso vai nos dar três resultados diferentes como podemos ver na imagem. O que isso significa?

O objeto __proto__ é, na verdade, uma referência a um outro objeto que tem várias outras propriedades. Por referência, quero dizer que ele não é uma cópia do valor apenas, mas sim do endereço de memória onde este objeto está armazenado, então qualquer operação sobre ele vai impactar no objeto original (porque eles tem o mesmo endereço de memória). Todos os objetos do tipo literal (lembra? Com {}) que nós criamos têm essa propriedade __proto__ apontando para esse mesmo objeto global.

Isso vai nos dar dois pontos importantes:

  • O __proto__ de um literal (com {}) é a mesma coisa do Object.prototype
  • O __proto__ de um Object.prototype é null

O que podemos fazer com isso é um assunto para a próxima parte deste artigo! Até lá!