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

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

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 null. 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.

const animal = {
  som: function() {
    console.log('Som genérico');
  }
};

const cachorro = Object.create(animal);
cachorro.latir = function() {
  console.log('Au au!');
};

cachorro.som(); // 'Som genérico' - herdado de animal
cachorro.latir(); // 'Au au!' - próprio do objeto

Construtores e o Operador new

Historicamente, JavaScript usava funções construtoras para criar objetos com prototypes. Quando você chama uma função com new, o JavaScript cria um novo objeto, define seu [[Prototype]] como o prototype 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 prototype de uma função construtora define o que será o [[Prototype]] de todos os objetos criados por ela. Note a diferença: [[Prototype]] é uma propriedade interna (acessível via Object.getPrototypeOf()), enquanto prototype é uma propriedade regular da função.

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('Toyota', 60);
console.log(carro.acelerar()); // 'Toyota acelerou para 70km/h'
console.log(Object.getPrototypeOf(carro) === Veiculo.prototype); // true

Evitando Armadilhas com Prototypes

Um erro comum é modificar Veiculo.prototype 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.

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('Alice');
const bob = new Usuario('Bob');
alice.adicionarAmigo('Charlie');
console.log(bob.amigos); // [] - Bob não foi afetado

Herança em Produção com Object.create() e Classes

Em produção moderna, preferimos Object.create() ou classes ES6. O Object.create() 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.

// Padrão com Object.create() - explícito e claro
const Animal = {
  fazer_som: function() {
    console.log('Som');
  }
};

const Gato = Object.create(Animal);
Gato.miar = function() {
  console.log('Miau!');
};

const meuGato = Object.create(Gato);
meuGato.nome = 'Felix';
meuGato.miar(); // 'Miau!'
meuGato.fazer_som(); // 'Som'

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

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('Maria', 28, 'JavaScript');
console.log(dev.apresentar()); 
// 'Olá, meu nome é Maria e programo em JavaScript'

Boas Práticas em Produção

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

// ❌ Ruim: modificar prototype global
Array.prototype.meuMetodo = function() {};

// ✅ Bom: estender com uma classe específica
class MinhaLista extends Array {
  meuMetodo() {
    // implementação
  }
}

// ✅ Bom: usar composição em vez de herança profunda
class ServidorWeb {
  constructor(banco, autenticador, logger) {
    this.banco = banco;
    this.autenticador = autenticador;
    this.logger = logger;
  }
}

Conclusão

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: 1) O prototype chain funciona por delegação — objetos herdam buscando propriedades em seus prototypes sucessivamente; 2) Use classes ES6 em produção — são mais seguras, legíveis e eliminam armadilhas; 3) Prefira composição para hierarquias complexas — evita acoplamento excessivo e torna o código mais flexível e testável.

Referências


Artigos relacionados