Prototype Chain Avançado: Object.create, getPrototypeOf e Herança Real: Do Básico ao Avançado Já leu

Object.create: A Base da Herança Real é o método fundamental para criar herança real em JavaScript. Diferente de construtores tradicionais, ele permite criar um objeto com um protótipo específico, oferecendo controle total sobre a cadeia de protótipos. Esse método foi introduzido no ES5 e revolucionou como implementamos herança, sendo a base das práticas modernas. Note como herda sem tê-lo definido. A busca passa pela cadeia até encontrar em . também aceita um segundo argumento com descritores de propriedade, permitindo configuração granular: Object.getPrototypeOf: Inspecionando a Cadeia retorna o protótipo de um objeto, permitindo navegar e inspecionar a cadeia de protótipos. É essencial para debugging e para construir sistemas que dependem de metaprogramação. Ao contrário de , é a forma padronizada e recomendada. Nível ${profundidade}: Combine com para modificar a cadeia dinamicamente, embora com cuidado devido ao impacto em performance: Herança Real com Múltiplos Níveis A verdadeira potência emerge ao criar hierarquias complexas que refletem relacionamentos reais do domínio. Diferente de herança

Object.create: A Base da Herança Real

Object.create() é o método fundamental para criar herança real em JavaScript. Diferente de construtores tradicionais, ele permite criar um objeto com um protótipo específico, oferecendo controle total sobre a cadeia de protótipos. Esse método foi introduzido no ES5 e revolucionou como implementamos herança, sendo a base das práticas modernas.

const animal = {
  fazer_som: function() {
    return "Som genérico";
  },
  dormir: function() {
    return "Zzzzz...";
  }
};

const cachorro = Object.create(animal);
cachorro.fazer_som = function() {
  return "Au au!";
};
cachorro.buscar = function() {
  return "Trazendo a bolinha!";
};

console.log(cachorro.fazer_som());  // "Au au!"
console.log(cachorro.dormir());     // "Zzzzz..." (herdado)
console.log(cachorro.buscar());     // "Trazendo a bolinha!"

Note como cachorro herda dormir() sem tê-lo definido. A busca passa pela cadeia até encontrar em animal. Object.create() também aceita um segundo argumento com descritores de propriedade, permitindo configuração granular:

const veiculo = {
  acelerar: function() { return "Acelerando..."; }
};

const carro = Object.create(veiculo, {
  portas: {
    value: 4,
    writable: true,
    enumerable: true
  },
  marca: {
    value: "Toyota",
    writable: false,
    enumerable: true
  }
});

console.log(carro.portas);   // 4
console.log(carro.marca);    // "Toyota"
carro.portas = 2;            // Permitido
// carro.marca = "Honda";    // Erro em strict mode

Object.getPrototypeOf: Inspecionando a Cadeia

Object.getPrototypeOf() retorna o protótipo de um objeto, permitindo navegar e inspecionar a cadeia de protótipos. É essencial para debugging e para construir sistemas que dependem de metaprogramação. Ao contrário de __proto__, é a forma padronizada e recomendada.

const pessoa = {
  saudar: function() { return "Olá!"; }
};

const joao = Object.create(pessoa);
joao.nome = "João";

const prototipo_joao = Object.getPrototypeOf(joao);
console.log(prototipo_joao === pessoa); // true
console.log(prototipo_joao.saudar());   // "Olá!"

// Navegar a cadeia completa
function mapear_cadeia(obj) {
  let atual = obj;
  let profundidade = 0;

  while (atual !== null) {
    console.log(`Nível ${profundidade}:`, Object.keys(atual));
    atual = Object.getPrototypeOf(atual);
    profundidade++;
  }
}

mapear_cadeia(joao);
// Nível 0: [ 'nome' ]
// Nível 1: [ 'saudar' ]
// Nível 2: []

Combine getPrototypeOf() com Object.setPrototypeOf() para modificar a cadeia dinamicamente, embora com cuidado devido ao impacto em performance:

const funcionario = {
  trabalhar: function() { return "Trabalhando..."; }
};

const gerente = {
  liderar: function() { return "Liderando time..."; }
};

let carlos = Object.create(funcionario);
console.log(carlos.trabalhar());  // "Trabalhando..."

// Mudar protótipo dinamicamente
Object.setPrototypeOf(carlos, gerente);
console.log(carlos.liderar());    // "Liderando time..."
// carlos.trabalhar();             // Erro: não está mais na cadeia

Herança Real com Múltiplos Níveis

A verdadeira potência emerge ao criar hierarquias complexas que refletem relacionamentos reais do domínio. Diferente de herança de classe (que é sintetizada), aqui a herança é literal: objetos herdam diretamente de outros objetos.

const ser_vivo = {
  respirar: function() { return "Respirando..."; },
  reproduzir: function() { return "Reproduzindo..."; }
};

const animal_avancado = Object.create(ser_vivo);
animal_avancado.mover = function() { return "Movendo..."; };
animal_avancado.comer = function() { return "Comendo..."; };

const mamifero = Object.create(animal_avancado);
mamifero.amamentar = function() { return "Amamentando..."; };

const cao = Object.create(mamifero);
cao.latir = function() { return "Au au!"; };
cao.nome = "Rex";

// Verificar a cadeia
console.log(cao.latir());      // "Au au!"
console.log(cao.amamentar());  // "Amamentando..."
console.log(cao.comer());      // "Comendo..."
console.log(cao.respirar());   // "Respirando..."

// Verificar onde cada método vive
console.log(Object.getPrototypeOf(cao) === mamifero);      // true
console.log(Object.getPrototypeOf(mamifero) === animal_avancado); // true
console.log(Object.getPrototypeOf(animal_avancado) === ser_vivo);  // true

Padrão OLOO (Objects Linking to Objects)

Este padrão, popularizado por Kyle Simpson, evita construtor functions totalmente:

const controle_animal = {
  init: function(nome, energia) {
    this.nome = nome;
    this.energia = energia;
    return this;
  },
  gastar_energia: function(qtd) {
    this.energia -= qtd;
    return this.energia > 0 ? `${this.nome} tem ${this.energia}% de energia` : "Sem energia!";
  }
};

const controle_passaro = Object.create(controle_animal);
controle_passaro.voar = function(km) {
  return this.gastar_energia(km * 2) + " - Voando!";
};

const passaro = Object.create(controle_passaro).init("Tweety", 100);
console.log(passaro.voar(10));      // "Tweety tem 80% de energia - Voando!"
console.log(passaro.gastar_energia(20)); // "Tweety tem 60% de energia"

Conclusão

Dominar Object.create() e Object.getPrototypeOf() revela que a verdadeira herança em JavaScript não precisa de classes ou construtores complexos. Primeiro aprendizado: herança real é objetos ligados a objetos; a cadeia de protótipos é uma busca linear por propriedades. Segundo aprendizado: Object.create() oferece controle fino, desde descritores até criação de hierarquias limpas, enquanto getPrototypeOf() permite inspecionar e modificar essa relação. Terceiro aprendizado: padrões como OLOO demonstram que código mais legível e flexível emerge quando abandonamos a mentalidade de classes e abraçamos a composição de objetos.

Referências


Artigos relacionados