Protótipos: a cadeia por trás dos objetos
Todo objeto em JavaScript tem, por baixo, um objeto pai de quem herda métodos prontos, como o próprio .map() dos arrays. Entender essa cadeia tira o mistério de onde vêm métodos que você nunca escreveu.
Estudar esta lição interativa →Você já usou array.map() e texto.toUpperCase() várias vezes ao longo deste curso, mas nunca escreveu o código desses métodos. De onde eles vêm? A resposta é uma peça central, e um tanto escondida, do JavaScript: o protótipo. Todo array, toda função e todo objeto literal que você cria tem, por baixo, uma ligação com outro objeto de onde ele "herda" métodos prontos, sem precisar copiá-los. (Textos são valores primitivos, não objetos, mas na hora de chamar um método como toUpperCase o JavaScript os trata por um instante como um objeto String e usa o protótipo dele.)
Quando você acessa meuArray.map, o JavaScript primeiro olha se map existe diretamente em meuArray. Como quase nunca existe (você não criou esse método), ele sobe para o protótipo do array, que é um objeto cheio de métodos prontos, como map, filter, push. Encontrou lá, usa. Esse processo de "subir e procurar" é a cadeia de protótipos, e ela pode ter vários elos, até chegar a um ponto final, o Object.prototype, de onde vêm até métodos básicos como .toString().
// Todo array "herda" métodos de Array.prototype, mesmo sem você escrevê-los.
const numeros = [1, 2, 3];
console.log(typeof numeros.map); // "function": existe, mas não foi criado por você
console.log(numeros.hasOwnProperty("map")); // false: veio do protótipo
console.log(numeros.hasOwnProperty("length")); // true: é própria do array
// A cadeia continua: Array.prototype também tem um protótipo (Object.prototype)
console.log(typeof numeros.hasOwnProperty); // "function": veio de Object.prototype, lá no topo da cadeiaPense na cadeia de protótipos como uma árvore genealógica de responsabilidades. Se você pergunta algo a um objeto e ele não sabe responder sozinho, ele pergunta ao "pai" (o protótipo), que pergunta ao "avô", e assim por diante, até alguém saber a resposta ou a busca chegar ao topo da árvore sem achar nada (aí o resultado é undefined, ou um erro, se você tentar chamar como função algo que não existe em lugar nenhum da cadeia).
O hasOwnProperty, usado no exemplo acima, é a ferramenta para distinguir os dois casos: ele responde true só quando a propriedade está diretamente no objeto, e false quando ela vem de algum ponto da cadeia acima. É uma pergunta que você vai fazer sempre que precisar ter certeza de que um dado é "seu" e não "emprestado" de algum protótipo.
const caneca = { cor: "azul" };
console.log(caneca.hasOwnProperty("cor")); // true: é própria do objeto
console.log(caneca.hasOwnProperty("toString")); // false: vem do protótipo padrão
console.log(typeof caneca.toString); // "function": veio de Object.prototype, você não escreveuComo Object.prototype é o topo da cadeia de todos os objetos comuns do seu programa, adicionar ou mudar algo nele (algo como Object.prototype.novoMetodo = ...) afeta literalmente todo objeto que existe, inclusive os de bibliotecas que você nem sabe que estão rodando. Isso é conhecido como "poluir" o protótipo e é considerado um dos maiores pecados capitais do JavaScript, por gerar bugs difíceis de rastrear em lugares completamente inesperados. As classes, que você vai ver na próxima lição, dão o mesmo poder (métodos compartilhados) de um jeito seguro e organizado.
Você não vai escrever protótipos manualmente com frequência (as classes cuidam disso por trás dos panos), mas entender essa cadeia explica um monte de comportamento que, sem ela, pareceria mágica: por que todo array "já vem" com .filter(), por que todo texto "já vem" com .trim(), e por que, no próximo módulo, uma classe inteira de objetos vai compartilhar os mesmos métodos sem gastar memória repetindo o código em cada instância.
// Sua vez! Investigue a cadeia de protótipos com hasOwnProperty e getPrototypeOf.
const caneca = { cor: "azul", capacidade: 300 };
// 1) "cor" é uma propriedade própria? Mostre no console:
console.log(caneca.hasOwnProperty("cor"));
// 2) "toString" é uma propriedade própria? (dica: não é)
console.log(caneca.hasOwnProperty("toString"));
// 3) Inspecione o protótipo diretamente:
console.log(Object.getPrototypeOf(caneca));Investigue a cadeia de protótipos e clique em Rodar.