Sobes.tech
Junior

Explique como funciona a herança em JavaScript.

sobes.tech IA

Resposta da IA

A herança em JavaScript é implementada através de protótipos. Cada objeto em JavaScript possui uma propriedade interna [[Prototype]] que referencia outro objeto. Quando tentamos aceder a uma propriedade ou método de um objeto que não está diretamente presente nele, o motor JavaScript procura essa propriedade ou método no protótipo desse objeto. Se não o encontrar lá, continua a busca ao longo da cadeia de protótipos, que é formada pelas ligações [[Prototype]], até atingir null (fim da cadeia).

Existem dois mecanismos principais de herança:

  1. Herança prototípica clássica (ES5 e anteriores): Baseada no uso de funções construtoras e na sua propriedade prototype.

    // Função construtora
    function Animal(name) {
      this.name = name;
    }
    
    // Adiciona método ao protótipo
    Animal.prototype.speak = function() {
      console.log(`${this.name} emite um som.`);
    };
    
    // Cria uma instância
    const dog = new Animal('Cão');
    
    // Chama método do protótipo
    dog.speak(); // Imprime: Cão emite um som.
    
    // O protótipo de cat é Animal.prototype
    console.log(Object.getPrototypeOf(dog) === Animal.prototype); // true
    
  2. Herança baseada em classes (ES6 e posteriores): Usa a sintaxe class e extends, que internamente ainda opera com protótipos.

    // Classe base
    class Animal {
      constructor(name) {
        this.name = name;
      }
    
      speak() {
        console.log(`${this.name} emite um som.`);
      }
    }
    
    // Subclasse que herda de Animal
    class Dog extends Animal {
      constructor(name, breeds) {
        super(name); // Chamada ao construtor da classe pai
        this.breeds = breeds;
      }
    
      bark() {
        console.log(`${this.name} late.`);
      }
    }
    
    // Cria uma instância da subclasse
    const myDog = new Dog('Bublik', ['Labrador', 'Collie']);
    
    // Chama métodos de Animal e Dog
    myDog.speak(); // Imprime: Bublik emite um som.
    myDog.bark();  // Imprime: Bublik late.
    
    // O protótipo de myDog é Dog.prototype
    console.log(Object.getPrototypeOf(myDog) === Dog.prototype); // true
    
    // O protótipo de Dog.prototype é Animal.prototype
    console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true
    

Conceitos-chave:

  • Cadeia de protótipos: Relação entre objetos através de seus [[Prototype]].

  • __proto__ (obsoleto/não padrão, mas útil para compreensão): Propriedade que permite aceder a [[Prototype]]. Não recomendado para uso em produção.

  • Object.getPrototypeOf(obj) : Forma padrão de obter o protótipo de um objeto.

  • Object.setPrototypeOf(obj, proto) : Forma padrão de alterar o protótipo de um objeto (não recomendado alterar o protótipo de objetos existentes após a sua criação).

  • instanceof : Operador para verificar se um objeto é uma instância de uma classe ou função construtora, verificando a sua cadeia de protótipos.

    console.log(myDog instanceof Dog);   // true
    console.log(myDog instanceof Animal); // true
    console.log(myDog instanceof Object); // true (no final da cadeia Object.prototype)
    
  • super : Usado em classes para chamar métodos ou o construtor da classe pai.

A diferença entre abordagens clássica e baseada em classes: As classes oferecem uma forma mais estruturada e sintaticamente conveniente de trabalhar com herança prototípica, semelhante a outras linguagens de programação. No entanto, o mecanismo fundamental permanece o mesmo — herança através de protótipos.