Class
No capítulo anterior, vimos como eram criadas classes antes do ES6. Abaixo está o exemplo de uma classe “animal”:
function Animal(nome, idade) {
this.nome = nome;
this.idade = idade;
}
Animal.prototype.comer = function() {
console.log(this.nome + " está comendo!");
}
Animal.prototype.fazerAniversario = function() {
this.idade++;
console.log(this.nome + " fez aniversário e agora tem " + this.idade + " anos!");
}
let animal = new Animal("Pato", 15);
animal.comer();
animal.fazerAniversario();
Porém, para quem está acostumado com outras linguagens, esse código é bem estranho. Por isso, no ES6, foi introduzido no Javascript uma nova forma de criar classes, sem precisar ficar tocando diretamente o mecanismo de prototype (embora ele ainda seja usado debaixo dos panos).
Bem, nós usamos classes para representar alguma coisa, um tipo de dado, algo do mundo real, num geral, é para isso que utilizamos.
Para criar nossa primeira classe em Javascript, usamos a palavra class e um nome, e também usamos { } para delimitar o corpo da nossa classe.
class Animal {
// corpo da nossa classe
}
Nós precisamos instanciar a nossa classe, para isso, usamos a palavra new, assim como em outras linguagens.
Para criar uma instância da classe Animal, fazemos:
let animal = new Animal();
Uma instância é um objeto, que pode ter suas propriedades e métodos. Iremos fazer isso agora.
Para definir as propriedades desse nosso objeto, precisamos de um construtor.
O construtor é um método especial que irá inicializar nossa classe, atribuindo os valores as nossas propriedades.
O método construtor é chamado no momento que usamos new para instanciar a nossa classe.
Vamos criar nosso primeiro construtor:
class Animal {
constructor() {
console.log("fui chamado");
this.idade = 1;
this.nome = "Bixinho";
}
}
let animal = new Animal();
console.log(animal.idade); // 1
console.log(animal.nome); // "Bixinho"
Se você executar o código acima, verá que quando instanciamos nossa classe com new, o método construtor será chamado, imprimindo “fui chamado” no console e inicializando nossas propriedades.
O método construtor sempre será chamado de constructor.
Perceba que o this no construtor, se refere à instancia da classe que foi criada, com o new (lembre que new cria um objeto vazio, que deve então ser preenchido, esse objeto novo é o this, por isso fazemos this.idade, this.nome).
Também é importante notar a ausência de function e de return. O método construtor não retorna nenhum valor, e dentro das nossas classes, não precisamos usar a palavra-chave function para criar um método.
Certo, criamos nosso primeiro construtor, e agora podemos criar diversas instâncias da classe Animal, como no exemplo abaixo:
let animal1 = new Animal();
let animal2 = new Animal();
let animal3 = new Animal();
console.log(animal1.idade); // 1
console.log(animal2.idade); // 1
console.log(animal3.idade); // 1
console.log(animal1.nome); // "Bixinho"
console.log(animal2.nome); // "Bixinho"
console.log(animal3.nome); // "Bixinho"
Perceba que todos os 3 animais tem a mesma idade e mesmo nome,isso acontece porque estamos atribuindo sempre os mesmos valores no nosso construtor:
constructor() {
console.log("fui chamado");
this.idade = 1;
this.nome = "Bixinho";
}
Porém, construtores podem (e geralmente) recebem parâmetros, assim como funções comuns.
Agora, reescrevendo a classe Animal recebendo a idade e o nome do nosso animal como parâmetro.
class Animal {
constructor(idade, nome) {
this.idade = idade;
this.nome = nome;
}
}
let animal = new Animal(15, "Patinho");
console.log(animal.idade); // 15
console.log(animal.nome); // "Patinho"
Perceba que precisamos passar os parâmetros para o método construtor na hora que instânciamos a classe, passamos 15 para a idade e “Patinho” para o nome.
Agora sim, podemos ter vários animais com nomes e idades distintas:
let animal1 = new Animal(7, "Tatu");
let animal2 = new Animal(20, "Peixe");
let animal3 = new Animal(27, "Tartaruga");
console.log(animal1.idade); // 7
console.log(animal2.idade); // 20
console.log(animal3.idade); // 27
console.log(animal1.nome); // "Tatu"
console.log(animal2.nome); // "Peixe"
console.log(animal3.nome); // "Tartaruga"
Classes também podem ter outros métodos, vamos criar um método mostraIdade para a nossa classe Animal.
class Animal {
constructor(idade, nome) {
this.idade = idade;
this.nome = nome;
}
mostraIdade() {
console.log("O animal " + this.nome + " tem " + this.idade + " anos.");
}
}
let animal = new Animal(15, "Patinho");
animal.mostraIdade();
Novamente, não usamos function para definir um método, basta por seu nome, os () para os parâmetros e as { } para delimitar o corpo do método.
O this será a instância do objeto que estamos chamando o método.
Métodos também podem receber parâmetros e retornar valores, assim como funções comuns.
Exemplo:
class Animal {
constructor(idade, nome) {
this.idade = idade;
this.nome = nome;
}
mostraIdade() {
console.log("O animal " + this.nome + " tem " + this.idade + " anos.");
}
setIdade(novaIdade) {
this.idade = novaIdade;
}
getIdade() {
return this.idade;
}
}
let animal = new Animal(15, "Patinho");
animal.mostraIdade();
animal.setIdade(16);
console.log(animal.getIdade()); // 16
Nesse exemplo acima, vimos que os métodos também podem alterar propriedades dos nosso objetos, como no método setIdade.
Na prática, um método comum também pode criar novas propriedades nos objetos, porém isso não é muito recomendável portanto, busque iniciar/criar todas as propriedades que você precisa no construtor da sua classe.
Espero que esteja tudo claro sobre classes em Javascript até aqui. Caso haja dúvida, recomendo reler novamente, sem pressa,pois a próxima parte vai depender dessa compreensão. Se você está compreendendo, pode continuar lendo sem problemas.
Ok, continuando.
Também podemos criar sub-classes usando extends. Sub-classes geralmente são versões mais especializadas de uma classe mais geral. Por exemplo, Animal é bem geral, porém Cachorro é mais especifico, pois cachorros latem, mordem, etc, agora não podemos dizer que qualquer animal late.
Vamos então, criar a sub-classe Cachorro, extendendo a classe Animal.
class Cachorro extends Animal {
//
}
Para criar nossa sub-classe, usamos extends e o nome da classe que desejamos tomar como base. Essa classe que tomamos como base (nesse caso, Animal) é normalmente chamada de “classe pai”. Seguindo a mesma nomenclatura, Cachorro é a classe-filha de Animal.
A classe-filha deve chamar o construtor da classe-pai, para isso, usamos a palavra-chave super.
class Cachorro extends Animal {
constructor(idade, nome) {
super(idade, nome); // chamando o construtor da classe "Animal"
}
}
Um detalhe muito importante: Sempre nos construtores das classes-filhas, a primeira linha do construtor deve ter SEMPRE a chamada ao construtor da classe pai.
Somente após podemos escrever o construtor da nosssa classe filha.
Certo, vamos criar uma instância de Cachorro.
class Cachorro extends Animal {
constructor(idade, nome) {
super(idade, nome); // chamando o construtor da classe "Animal"
}
}
let cachorro = new Cachorro(9, "snoop dog");
cachorro.mostraIdade();
Novamente, usamos new para criar nossa instância, passamos os parâmetros do construtor.
Um detalhe importante sobre classes filhas: classes filhas “herdam” os métodos das classes pais, por isso, podemos chamar o método mostraIdade na nossa instância da classe Cachorro. Além dos métodos, as propriedades também são “herdadas”, portanto podemos tranquilamente acessar a propriedade idade ou nome na nossa instância da classe Cachorro. Também podemos acessar métodos e propriedades da classe-pai dentro da classe-filha, através do this.
Ok, métodos e propriedades são herdadas. Mas também podemos criar novos métodos e propriedades! Vamos criar uma propriedade ração_favorita e um método latir para o nosso Cachorro.
class Cachorro extends Animal {
constructor(idade, nome, racao_favorita) {
super(idade, nome); // chamando o construtor da classe "Animal"
this.racao_favorita = racao_favorita;
}
latir() {
console.log("AU AU AU AU");
}
}
let cachorro = new Cachorro(9, "snoop dog", "Pedigree");
cachorro.mostraIdade();
cachorro.latir();
Perceba que agora precisamos passar o parâmetro racao_favorita para o construtor da classe Cachorro! E também temos o nosso novo método: latir.
Além de criar novos métodos e propriedades, podemos reescrever métodos já existentes, basta criar um novo método com o mesmo nome do método que você deseja reescrever. Vamos reescrever o método mostraIdade.
class Cachorro extends Animal {
constructor(idade, nome, racao_favorita) {
super(idade, nome); // chamando o construtor da classe "Animal"
this.racao_favorita = racao_favorita;
}
latir() {
console.log("AU AU AU AU");
}
mostraIdade() {
console.log("O cachorro " + this.nome + " tem " + this.idade + " cachorro-anos.");
}
}
let cachorro = new Cachorro(9, "snoop dog", "Pedigree");
cachorro.mostraIdade();
cachorro.latir();
Simples assim! Agora mostraIdade mostra a idade do nosso Cachorro em cachorro-anos.
Embora seja um caso de uso bem especifico, quando sobreescrevemos um método, se for necessário, ainda é possível chamar o método pai utilizando super.nomeDoMetodoPai().
Vamos mostrar um pequeno exemplo:
class Animal {
constructor(nome) {
this.nome = nome;
}
falar() {
console.log(this.nome + ' está falando algo...');
}
}
class Leao extends Animal {
falar() {
super.falar();
console.log(this.nome + ' solta um rugido.');
}
}
let leao = new Leao('Simba');
leao.falar();
Esse código imprime “Simba está falando algo…” e “Simba solta um rugido.”
Classes serão muito utilizadas quando formos trabalhar com React (ou escrever código JS atualmente), portanto, é importante entender bem como elas funcionam, e como podemos usar elas no nosso código.