JS-0402 - JavaScript Completo ES6 - Prototype
O núcleo do JavaScript
O Prototype, protótipo, é uma propriedade das funções, adicionado como objeto sempre que uma função é criada. Esta propriedade é a que permite a herança de outras propriedades e métodos dos objetos construtores aos criados a partir deles. A herança é encadeada, permitindo o acesso até os tipos de dados nativos do JS.
Por essa razão o JavaScript é frequentemente descrito como uma linguagem baseada em protótipos. As propriedades e os métodos são definidos na propriedade prototype nas funções construtoras dos objetos, não nas próprias instâncias do objeto, existindo um link entre a instância do objeto e seu protótipo (propriedade __proto__derivada da propriedade prototype no construtor), e as propriedades e os métodos são encontrados percorrendo a cadeia de protótipos.
No exemplo abaixo será utilizado a mesma função construtora do post anterior (funções construtoras). O prototype da função "Animal" retorna o seu construtor Object. Já o objeto "pessoa", construido a partir de "Animal" retorna undefined, pois prototype é uma propriedade somente das funções.
O protótipo das funções contém propriedades e métodos de si, bem como o protópido do seu construtor que também contém o mesmo relacionado a si e assim por diante. Contudo, há a possbilidade de adicionar novas propriedades e métodos ao objeto prototype (tudo é objeto). Ex:
Esta nova função não estará presente no objeto "pessoa" criado, mas será acessível pelo prototype, pois "pessoa" possui acesso aos métodos e propriedades do protótipo deste construtor através da propriedade __proto__. Ex:
Isto garante que as propriedades e métodos herdados sejam instanciados uma única vez na memória, não sendo criados todas as vezes que um novo objeto é criado. É papel da engine fazer a busca das propriedades e métodos do __proto__ do objeto e não devemos falar com __proto__ diretamente. A engine realiza a busca primeiro no objeto, depois no construtor sucessivamente dentro da cadeia de protótipos, até o protótipo de Object.
Todos os tipos de dados são criados utilizando construtores, chamados de Construtores Nativos. Os construtores possuem um protótipo com propriedades e métodos que poderão ser acessadas pelo tipo de dado. As variáveis, mesmo quando não tem seu construtor declarado, como tudo é objeto, momentaneamente se tornam objeto de algum tipo de acordo com o seu valor, possibilitando o acesso pelo prototipo do seu construtor nativo. Ex:
No exemplo acima, é possível visualizar os métodos e propriedades dos construtores nativos através de prototype e isto é muito útili quando é preciso explorar algum tipo destes, exceto os tipos Nulle undefined, que não possuem prototype. Symbol retorna o prototype de Object.
É muito comum, principalmente em códigos antigos, o uso direto de funções do protótipo do construtor Array, para permitir que objetos de diferentes tipos, principalmente NodeList, possa utilizar os métodos de Array. Atualmente é possível também utilizando o método Array.from().
Nos objetos nativos existem métodos linkados diretamente ao objeto e outros linkados ao protótipo que não permitirão o uso direto, sem o protótipo. Para retornar a lista contendo propriedades e métodos de um objeto (de si, não de seu construtor), podemos utilizar o método getOwnPropertyNames() de Object. Ex:
Agora vem algo importante!!
Os métodos e propriedades acessados com o operador dot, o ponto, são referentes ao tipo de dados que é retornado antes desse. Para verificar qual o tipo de dado retornado e um correto uso das propriedades e métodos dos seus construtores, utiliza-se o nome da propriedade seguido de .constructor.name, como na penúltima linha da imagem abaixo: