Implementando Herança em JavaScript
Aprenda como implementar herança em JavaScript utilizando: prototype, call e Object.create()
Entendendo o que é uma classe
Antes de explicar como funciona a herança em JavaScript, devemos entender primeiro o que é uma classe
Uma classe nada mais é do que um modelo para se criar objetos. Esse modelo descreve as características e comportamentos que todos objetos terão.
As características são os atributos e os comportamentos são os métodos(funções) das classes.
Imagine se a Terra começasse a abrigar alienígenas e os governos quisessem manter o controle de todos que estivessem vivendo aqui e para cada alienígena tivéssemos de saber seu nome, idade e planeta. Criando um objeto teríamos:
name : 'IOAS3356',
age : 159,
planet : 'Pluto'
Mas isso seria inviável pois teríamos diversos alienígenas de diferentes planetas e teríamos que repetir esse código em todo nosso programa. Então poderíamos criar uma classe para esse fim.
Antes de sair o ECMAScript 6, o JavaScript apenas simula uma classe utilizando uma função. Essa função é conhecida como função construtora e como o próprio nome indica, ela é responsável por criar novos objetos. A chamada para essa função se diferencia de funções comuns, pois deve ser feita através da palavra reservada new.
Vamos agora criar a nossa classe Alien para nosso SRART - Sistema de Registro de Alienígenas Residentes na Terra:
// Função construtura responsável por criar objetos Alien
function Alien(name, age, planet) {
// Atributos da nossa classe
// Método da nossa classe
console.log('#$#: ' + this.name + '! OADPL$%)@@#: ' + this.planet);
Agora que criamos a nossa classe, podemos criar diversos objetos utilizando ela:
// Criamos um objeto com os valores que passamos para a função construtura
var alien_one = new Alien('IOAS3356', 159, 'Pluto');
// Com nosso objeto criado, podemos acessar todos seus atributos e até mesmo fazer chamadas a seus métodos
alien_one.greeting();
// Criando outro objeto Alien
var alien_two = new Alien('ADVQ4490', 227, 'Mars');
alien_two.greeting();
Vocês podem ver que utilizei a palavra reservada this na hora de criar os atributos e o método de nossa classe. Fiz isso, pois essa palavra está referenciando o objeto que será criado. Como podem ver o método foi colocado dentro de nossa classe, o que não é o correto a se fazer, pois dessa maneira cada objeto que criarmos terá um método. Vamos ver a forma correta de se fazer mais adiante.
Primeiramente vamos entender o que é herança:
É um mecanismo onde uma classe pai (superclasse) passa suas características para classes filhas (subclasses). Uma classe que herda as características de outra pode ter características próprias além das herdadas. Como no JavaScript as classes são simuladas, não podemos implementar a herança como outras linguagens orientadas a objetos, devemos utilizar algumas técnicas que veremos a seguir.
Herança utilizando prototype, call e Object.create()
Todas as funções no JavaScript contém a propriedade prototype que vem vazia, mas pode ser alterada. Essa propriedade é tratada como um objeto e nela podemos anexar atributos e métodos.
Alterando a nossa classe Alien criada anteriormente para utilizar o prototype, ficaria assim:
// Atributos da nossa classe (anexados diretamenta na função)
// Anexando método ao prototype da nossa função construtura
Alien.prototype.greeting = function() {
// Criando objetos com a nossa função construtura e acessando seu método
Como vocês podem ver, a instanciação de nossos objetos e a chamada do método greeting() não foi alterada, mas então o que mudou? Antes teríamos dois objetos, cada um com seu método greeting() e agora utilizando o prototype, o método greting() ficou anexado na nossa função construtora e temos apenas um método. Os métodos anexados no prototype podem ainda acessar os valores do objeto declarados dentro da função construtora como mostrado no exemplo acima.
Imagine agora que o nosso sistema SRART deve separar os alienígenas por espécie e que além dos dados gravados precisamos também de registrar dados específicos de cada espécie. Iremos fazer o uso do prototype para transformar a nossa classe Alien em uma Classe Pai e iremos criar duas Classe Filhas:
// Função construtura responsável por criar objetos Gray
function Gray(name, age, planet, language) {
Alien.call(this, name, age, planet);
Gray.prototype = Object.create(Alien.prototype);
// Função construtura responsável por criar objetos Reptilian
function Reptilian(name, age, planet, skinColor) {
Reptilian.prototype = Object.create(Alien.prototype);
A implementação da herança acontece nesses dois momentos:
Alien.call(this, name, age, planet)
Quando passamos o this como primeiro parâmetro, ele vai até a nossa classe Alien e faz com que todas as propriedades com this apontem agora para nossa Classe Filha, dessa maneira todas as propriedades da classe Alien estão disponíveis em nossas Classes Filhas. É muito importante se lembrar de sempre passar o this como primeiro parâmetro ao fazer isso.
Gray.prototype = Object.create(Alien.prototype) / Reptilian.prototype = Object.create(Alien.prototype)
Com isso fazemos com que o prototype de nossas Classes Filhas herdem todas as propriedades do prototype da nossa Classe Pai.
Utilizando as propriedades herdadas
Agora vamos criar um novo método que vai puxar o relatório de um alienígena e anexar no prototype das nossas Classes Filhas:
// Método para imprimir relatório de um Gray
Gray.prototype.showRegistry = function() {
console.log('Planet: ' + this.planet);
// Método para imprimir relatório de um Reptilian
Reptilian.prototype.showRegistry = function() {
console.log('Skin Color: ' + this.skinColor);
Agora podemos cadastrar em nosso sistema SRART novos alienígenas utilizando nossas Classes Filhas ao invés de nossa Classe Pai e puxar o relatório das mesmas:
// Criando nosso primeiro Gray
var gray_one = new Gray('IOAS3356', 159, 'Pluto', 'Graynian');
gray_one.showRegistry();
// Criando nosso primeiro Reptilian
var reptilian_one = new Reptilian('ADVQ4490', 227, 'Mars', 'Red');
reptilian_one.showRegistry();
Há outras formas de se implementar a herança em JavaScript, até mesmo formas mais simples com o ECMAScript 6, mas deixarei para falar sobre essas formas em um próximo artigo! xD