JavaScript

Como Usar Prototype Chain em JavaScript: Herança Baseada em Protótipos em Produção

6 min de leitura

Como Usar Prototype Chain em JavaScript: Herança Baseada em Protótipos em Produção

Entendendo o Prototype Chain O prototype chain é o mecanismo fundamental de herança em JavaScript. Quando você acessa uma propriedade em um objeto, o JavaScript primeiro procura naquele objeto. Se não encontrar, busca no prototype do objeto, depois no prototype do prototype, e assim sucessivamente até chegar a . Essa cadeia de prototypes é o que chamamos de prototype chain. Diferentemente de linguagens baseadas em classes, JavaScript usa herança baseada em protótipos onde objetos herdam diretamente de outros objetos. Entender isso é crucial para escrever código eficiente e evitar armadilhas comuns em produção. Vamos explorar como isso funciona na prática. Construtores e o Operador Historicamente, JavaScript usava funções construtoras para criar objetos com prototypes. Quando você chama uma função com , o JavaScript cria um novo objeto, define seu como o da função construtora, e executa a função naquele contexto. Essa abordagem ainda é amplamente usada em código legado e é essencial compreender. A propriedade de uma função construtora define

<h2>Entendendo o Prototype Chain</h2>

<p>O prototype chain é o mecanismo fundamental de herança em JavaScript. Quando você acessa uma propriedade em um objeto, o JavaScript primeiro procura naquele objeto. Se não encontrar, busca no prototype do objeto, depois no prototype do prototype, e assim sucessivamente até chegar a <code>null</code>. Essa cadeia de prototypes é o que chamamos de prototype chain.</p>

<p>Diferentemente de linguagens baseadas em classes, JavaScript usa herança baseada em protótipos onde objetos herdam diretamente de outros objetos. Entender isso é crucial para escrever código eficiente e evitar armadilhas comuns em produção. Vamos explorar como isso funciona na prática.</p>

<pre><code class="language-javascript">const animal = {

som: function() {

console.log(&#039;Som genérico&#039;);

}

};

const cachorro = Object.create(animal);

cachorro.latir = function() {

console.log(&#039;Au au!&#039;);

};

cachorro.som(); // &#039;Som genérico&#039; - herdado de animal

cachorro.latir(); // &#039;Au au!&#039; - próprio do objeto</code></pre>

<h2>Construtores e o Operador <code>new</code></h2>

<p>Historicamente, JavaScript usava funções construtoras para criar objetos com prototypes. Quando você chama uma função com <code>new</code>, o JavaScript cria um novo objeto, define seu <code>[[Prototype]]</code> como o <code>prototype</code> da função construtora, e executa a função naquele contexto. Essa abordagem ainda é amplamente usada em código legado e é essencial compreender.</p>

<p>A propriedade <code>prototype</code> de uma função construtora define o que será o <code>[[Prototype]]</code> de todos os objetos criados por ela. Note a diferença: <code>[[Prototype]]</code> é uma propriedade interna (acessível via <code>Object.getPrototypeOf()</code>), enquanto <code>prototype</code> é uma propriedade regular da função.</p>

<pre><code class="language-javascript">function Veiculo(marca, velocidade) {

this.marca = marca;

this.velocidade = velocidade;

}

Veiculo.prototype.acelerar = function() {

this.velocidade += 10;

return ${this.marca} acelerou para ${this.velocidade}km/h;

};

const carro = new Veiculo(&#039;Toyota&#039;, 60);

console.log(carro.acelerar()); // &#039;Toyota acelerou para 70km/h&#039;

console.log(Object.getPrototypeOf(carro) === Veiculo.prototype); // true</code></pre>

<h3>Evitando Armadilhas com Prototypes</h3>

<p>Um erro comum é modificar <code>Veiculo.prototype</code> depois de criar instâncias ou compartilhar arrays/objetos entre instâncias. Se você quer propriedades únicas por instância, defina-as no construtor, não no prototype. Propriedades compartilhadas (métodos) vão no prototype.</p>

<pre><code class="language-javascript">function Usuario(nome) {

this.nome = nome;

this.amigos = []; // Correto: cada instância tem seu próprio array

}

Usuario.prototype.adicionarAmigo = function(amigo) {

this.amigos.push(amigo); // Correto: método no prototype

};

const alice = new Usuario(&#039;Alice&#039;);

const bob = new Usuario(&#039;Bob&#039;);

alice.adicionarAmigo(&#039;Charlie&#039;);

console.log(bob.amigos); // [] - Bob não foi afetado</code></pre>

<h2>Herança em Produção com <code>Object.create()</code> e Classes</h2>

<p>Em produção moderna, preferimos <code>Object.create()</code> ou classes ES6. O <code>Object.create()</code> oferece controle explícito sobre o prototype, tornando o código mais legível. Já as classes ES6 são açúcar sintático sobre construtores, mas facilitam a escrita e compreensão da herança.</p>

<pre><code class="language-javascript">// Padrão com Object.create() - explícito e claro

const Animal = {

fazer_som: function() {

console.log(&#039;Som&#039;);

}

};

const Gato = Object.create(Animal);

Gato.miar = function() {

console.log(&#039;Miau!&#039;);

};

const meuGato = Object.create(Gato);

meuGato.nome = &#039;Felix&#039;;

meuGato.miar(); // &#039;Miau!&#039;

meuGato.fazer_som(); // &#039;Som&#039;</code></pre>

<p>Para hierarquias mais complexas, classes ES6 são superiores. Elas compilam para o mesmo prototype chain, mas com sintaxe muito mais clara. Use <code>extends</code> para herança e <code>super</code> para acessar o prototype pai.</p>

<pre><code class="language-javascript">class Pessoa {

constructor(nome, idade) {

this.nome = nome;

this.idade = idade;

}

apresentar() {

return Olá, meu nome é ${this.nome};

}

}

class Desenvolvedor extends Pessoa {

constructor(nome, idade, linguagem) {

super(nome, idade);

this.linguagem = linguagem;

}

apresentar() {

return ${super.apresentar()} e programo em ${this.linguagem};

}

}

const dev = new Desenvolvedor(&#039;Maria&#039;, 28, &#039;JavaScript&#039;);

console.log(dev.apresentar());

// &#039;Olá, meu nome é Maria e programo em JavaScript&#039;</code></pre>

<h3>Boas Práticas em Produção</h3>

<p>Em produção, <strong>sempre use classes ES6</strong> para código novo. Elas são mais seguras, legíveis e os transpiladores as suportam completamente. Evite modificar prototypes de objetos globais como <code>Array.prototype</code> ou <code>Object.prototype</code>. Use <code>Object.freeze()</code> em prototypes críticos para evitar alterações acidentais. Sempre prefira composição quando a hierarquia fica profunda (mais de 3 níveis).</p>

<pre><code class="language-javascript"></code></pre>

<h2>Conclusão</h2>

<p>O prototype chain é o coração de JavaScript e dominar sua herança baseada em protótipos é essencial para código profissional. Os três pontos principais que deve reter: <strong>1) O prototype chain funciona por delegação</strong> — objetos herdam buscando propriedades em seus prototypes sucessivamente; <strong>2) Use classes ES6 em produção</strong> — são mais seguras, legíveis e eliminam armadilhas; <strong>3) Prefira composição para hierarquias complexas</strong> — evita acoplamento excessivo e torna o código mais flexível e testável.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain" target="_blank" rel="noopener noreferrer">MDN - Inheritance and the prototype chain</a></li>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes" target="_blank" rel="noopener noreferrer">MDN - Classes</a></li>

<li><a href="https://www.oreilly.com/library/view/javascript-the-definitive/9781491952016/" target="_blank" rel="noopener noreferrer">JavaScript: The Definitive Guide - David Flanagan</a></li>

<li><a href="https://github.com/getify/You-Dont-Know-JS" target="_blank" rel="noopener noreferrer">You Don&#039;t Know JS Yet - Kyle Simpson</a></li>

<li><a href="https://tc39.es/ecma262/#sec-objects" target="_blank" rel="noopener noreferrer">ECMAScript Specification - Objects</a></li>

</ul>

Comentários

Mais em JavaScript

Guia Completo de Interfaces e Type Aliases em TypeScript na Prática
Guia Completo de Interfaces e Type Aliases em TypeScript na Prática

Fundamentos: Type Aliases vs Interfaces Type Aliases e Interfaces são dois me...

Dominando Módulos Nativos do Node.js: fs, path, os e crypto em Projetos Reais
Dominando Módulos Nativos do Node.js: fs, path, os e crypto em Projetos Reais

Introdução aos Módulos Nativos do Node.js O Node.js fornece diversos módulos...

Callbacks em JavaScript: O Padrão Original de Assincronismo: Do Básico ao Avançado
Callbacks em JavaScript: O Padrão Original de Assincronismo: Do Básico ao Avançado

Callbacks em JavaScript: O Padrão Original de Assincronismo O que é um Callba...