Herança em JavaScript

(Last Updated On: 28 de dezembro de 2016)

Temos um construtor Conta que define um atributo saldo e um método deposita:

function Conta() {
    this.saldo = 0;
    this.deposita = function(valor) {
        this.saldo += valor;
    };
}

var contaCorrente = new Conta();
contaCorrente.deposita(1000);
contaCorrente.saldo; //1000

Legal, funciona!

Agora, nosso cliente precisa de uma conta poupança: um tipo de conta que atualiza o saldo de acordo com um índice.

Fácil: basta criarmos um construtor ContaPoupanca, copiar e colar todo o código de Conta e definir um novo método atualiza:

function ContaPoupanca() {
    //copiando da Conta...
    this.saldo = 0;
    this.deposita = function(valor) {
        this.saldo += valor;
    };
    //o método a mais...
    this.atualiza = function(indice) {
        this.saldo += this.saldo * indice;
    };
}

var contaPoupanca = new ContaPoupanca();
contaPoupanca.deposita(1000);
contaPoupanca.saldo; //1000
contaPoupanca.atualiza(0.05);
contaPoupanca.saldo; //1050

Poxa, mas copiar e colar é ruim.
Toda vez que mudarmos ou ampliarmos Conta, temos que lembrar de copiar para ContaPoupanca.

O ideal é reutilizar o código de Conta, fazendo com que ContaPoupanca seja um tipo especial de Conta, com tudo o que Conta tem mais o método atualiza.

Em linguagens orientadas a objetos, essa ideia de criar um tipo especial baseado em outro é chamada de Herança. A linguagem JavaScript é orientada a objetos e tem suporte a herança.

A maioria das linguagens tem classes, com alguma maneira de estendê-las. Mas o JavaScript não é uma linguagem “clássica” e herança em JavaScript é um pouco diferente, baseada em protótipos.

Primeiro, crio o construtor ContaPoupanca sem as propriedades que quero herdar, apenas com o método a mais:

function ContaPoupanca() {
    //não preciso copiar de Conta...
    this.atualiza = function(indice) {
        this.saldo += this.saldo * indice;
    };
}

Então, usando o prototype de ContaPoupanca, digo que quero copiar tudo o que há em Conta para dentro de ContaPoupanca.

ContaPoupanca.prototype = new Conta(); 
ContaPoupanca.prototype.constructor = ContaPoupanca; 

Na verdade, existem outras maneiras de copiar as propriedade de Conta para o prototype de ContaPoupanca. A maneira acima é a mais comum.

Finalmente, podemos criar uma conta poupança e usar tudo de uma conta, além de atualizar:

var contaPoupanca = new ContaPoupanca();
contaPoupanca.deposita(1000);
contaPoupanca.atualiza(0.05);
contaPoupanca.saldo; //1050

Na última versão do JavaScript, o EcmaScript 2015, você pode definir herança usando a palavra-chave extends, presente em muitas outras linguagens:

class Conta {
    constructor() {
        this.saldo = 0;
    }
    deposita(valor) {
        this.saldo += valor;
    }
}
class ContaPoupanca extends Conta {
    atualiza(indice) {
        this.saldo += this.saldo * indice;
    }
}

A linguagem Javascript tem vários recursos poderosos! Prepare-se para sua carreira de Desenvolvedor Javascript com os cursos de Front-End da Alura.

FIQUE POR DENTRO

  • Amigo, esse modelo proposto não reflete bem a implementação de class do ES6. Existe alguns defeitos no modelo, está faltando usar o prototype corretamente e ajustar o constructor na herança… nesse gist eu mostro os defeitos e a solução também dos mesmos:

    https://gist.github.com/gtkatakura/e286135812f083687532a39b5179b289

    • Olá, gtkatakura. Valeu pela contribuição!

      Realmente, esqueci de ajustar o constructor no prototype de ContaPoupanca. Corrigi acima.

      No seu gist, faltou o seguinte trecho logo depois da definição de ContaPoupancaES5.

      ContaPoupancaES5.prototype = new ContaES5();
      ContaPoupancaES5.prototype.constructor = ContaPoupancaES5;
      

      Mas, realmente, não terá o mesmo comportamento da class do ES6, com alguns problemas na cadeia de prototype.
      Ainda assim, é uma maneira bem comum de usar herança em JS.

      Vou colocar a sua solução aqui, que resolve os problemas de prototype:

      function ContaOK() {}
        Object.assign(ContaOK.prototype, {
          saldo: 0
          , deposita: function (valor) {
            this.saldo += valor;
          }
        });
        function inherit(child, parent, members) {
          return Object.assign(Object.create(parent.prototype), members, {
            constructor: child
          });
        }
        function ContaPoupancaOK() {}
          inherit(ContaPoupancaOK, ContaOK, {
            atualiza: function (indice) {
              this.saldo += this.saldo * indice;
          }
        });
      
  • Eu me sinto cada vez mais seguro em apostar meus estudos em javascript.

Próximo ArtigoO que é Push Notification?