Javascript
Tudo o que você precisa saber sobre classes no Javascript
A orientação a objetos nunca foi um ponto forte do Javascript, porém, esse contexto mudou um pouco com a chegada das classes, que funcionam como um açúcar sintático para a real POO (Programação Orientada a objetos) da linguagem, além do mais, isso traz uma sofisticação a sintaxe da linguagem, permitindo com que você crie projetos muito mais organizados e bem elaborados utilizando todos os recursos possíveis de POO. E neste artigo, eu irei te mostrar como trabalhar com as classes no Javascript, do zero absoluto, então pegue uma boa xícara de café e vamos começar.
O Básico
Antes do ES6, você poderia(e ainda pode) criar estruturas parecidas com classes no Javascript, porém utilizando funções. Na minha opinião pessoal, eu nunca achei legal essa idéia de criar representação de classes utilizando funções, isso deixava toda a semântica do código extremamente confusa, agora após a chegada do ES6, vamos ver como criar uma classe “de verdade” no javascript.
Como criar classes
Para criar uma classe é muito simples. Tudo o que você precisa fazer é iniciar digitando a palavra chave class e após isso definir o nome de sua classe, como qualquer outra linguagem orientada a objetos, veja o exemplo abaixo:
class Pessoa{ }
Como você pode ver esta é a estrutura básica de uma classe no Javascript, nada fora do comum. E como sabemos, dentro de uma classe você precisa definir atributos e métodos, mas como fazemos isso?
Como definir atributos
A forma de criar atributos de uma classe no Javascript é completamente fora do padrão de outras linguagens orientadas a objetos, nas quais você declara-os diretamente no corpo da classe. Aqui, o processo é um pouco diferente, já que você deve declarar seus atributos dentro do método construtor, que é um método que toda classe deve ter, e que é o primeiro a ser executado sempre quando você cria um novo objeto daquela classe. Veja abaixo como definir o método construtor:
class Pessoa{ constructor(){ } }
Agora, dentro deste método você pode definir seus atributos desta forma:
class Pessoa{ constructor(){ this.atributo1; this.atributo2; } }
Em resumo, sempre quando você for declarar atributos, faça isso dentro do método construtor e utilizando a palavra chave this antes do nome do atributo, este que é o padrão do Javascript. Além disso, outra coisa que você pode fazer, é passar parâmetros para seus atributos dentro do construtor, veja abaixo:
class Pessoa{ constructor(param1, param2){ this.atributo1 = param1; this.atributo2 = param2; } }
Desta forma você pode definir valores para sua classe durante a criação de uma instância dela(veremos esse processo mais a frente). Além de atributos, uma classe também deve ter métodos que representam ações que aquela entidade pode fazer, mas como definir métodos no Javascript?
Definindo métodos
A definição de métodos é um processo muito simples dentro das classes do ES6, pois eles são definidos diretamente no corpo da classe quase da mesma forma de funções só que sem precisar utilizar a palavra chave function, veja o exemplo abaixo:
class Pessoa{ constructor(param1, param2){ this.atributo1 = param1; this.atributo2 = param2; } metodo1(){ console.log("Classes são legais!"); } metodo2(param1){ console.log("isso é um parametro: " + param1 + " como em funções convencionais!"); } metodoDeSoma(a, b){ return a + b; } }
Como você viu acima, os métodos são basicamente funções que existem dentro de classes, eles funcionam exatamente da mesma forma de funções convencionais, podem receber parâmetros, printar informações, retornar dados e etc.
Criando um objeto
E agora que você já sabe o básico, está na hora de aprender a como criar uma instância de uma classe. Para fazer isso é muito simples, você só precisa seguir o exemplo abaixo:
var victor = new Pessoa(); var lima = new Pessoa();
Ou seja, primeiramente você define uma variável que vai armazenar uma instância, e após isso você passa para essa variável uma nova instância que você cria com a palavra chave new e o nome da classe seguido de parênteses. Além dessa forma básica de criar objetos, você ainda pode passar parâmetros para sua classe durante a instanciação, você se lembra que era possível definir parâmetros no construtor?
class Pessoa{ constructor(param1, param2){ this.atributo1 = param1; this.atributo2 = param2; } }
Então, você pode definir estes parâmetros durante a criação do objeto desta forma:
var victor = new Pessoa("Valor do param1", "Valor do param1"); var lima = new Pessoa(20, "Valor qualquer");
Bom agora que você já sabe como criar instâncias, agora é importante aprender a como acessar métodos e atributos, e acredite ou não, este é o processo mais simples de todos, veja o exemplo abaixo:
var victor = new Pessoa("Valor do param1", "Valor do param1"); var lima = new Pessoa(20, "Valor qualquer"); console.log(victor.atributo1) console.log(victor.atributo2) victor.metodo1(); victor.metodo2("Foo"); console.log(victor.metodoDeSoma(1,1)); console.log(lima.atributo1) console.log(lima.atributo2) lima.metodo1(); lima.metodo2("Foo"); console.log(lima.metodoDeSoma(1,1));
Ou seja, tanto para acessar métodos como atributos, você só precisa digitar o nome do objeto seguido de ponto e o nome do elemento. Além de todo estes passos básicos para manipular classes e objetos, ainda existem mais recursos na linguagem que iremos ver mais a seguir, um deles é o encapsulamento, que é um dos pilares da orientação a objetos.
Encapsulamento
Como você já viu quando estava estudando orientação a objetos, o encapsulamento é um dos assuntos mais importantes, pois ele vai garantir a integridade de seus objetos. A boa notícia é que ele existe no Javascript, e a má notícia é que o encapsulamento dentro da linguagem é puramente fictício e funciona apenas como açúcar sintático.
Um dos principais recursos do encapsulamento são os getters e setters, e para criá-los, primeiramente você precisa ter um atributo, e após isso crie dois métodos com o mesmo nome do atributo e coloque as palavra chave get e set na frente de cada um respectivamente. Veja o exemplo abaixo:
class Pessoa{ constructor(){ this.atributo1; } get atributo1(){ return this.atributo1; } set atributo1(oi){ this.atributo1 = oi; } }
Todo setter deve ter um parâmetro que irá receber os dados, já todo getter não deve receber nenhum parâmetro e deve sempre retornar alguma coisa. A vantagem de utilizar esta estrutura é que você consegue tratar toda saída e entrada de dados na sua classe, porém, o encapsulamento no Javascript tem um problema, que gera este erro aqui abaixo:
RangeError: Maximum call stack size exceeded at Pessoa.get atributo1 [as atributo1] (C:\Users\victo\Desktop\teste.js:5:18) at Pessoa.get atributo1 [as atributo1] (C:\Users\victo\Desktop\teste.js:6:21) at Pessoa.get atributo1 [as atributo1] (C:\Users\victo\Desktop\teste.js:6:21) at Pessoa.get atributo1 [as atributo1] (C:\Users\victo\Desktop\teste.js:6:21) at Pessoa.get atributo1 [as atributo1] (C:\Users\victo\Desktop\teste.js:6:21) at Pessoa.get atributo1 [as atributo1] (C:\Users\victo\Desktop\teste.js:6:21) at Pessoa.get atributo1 [as atributo1] (C:\Users\victo\Desktop\teste.js:6:21) at Pessoa.get atributo1 [as atributo1] (C:\Users\victo\Desktop\teste.js:6:21) at Pessoa.get atributo1 [as atributo1] (C:\Users\victo\Desktop\teste.js:6:21) at Pessoa.get atributo1 [as atributo1] (C:\Users\victo\Desktop\teste.js:6:21)
Este é o grande pulo do gato do encapsulamento no Javascript, pois não tem como você ter getters e setters com os mesmos nomes de um atributo. Para resolver este erro você precisa renomear o seu atributo encapsulado, veja o exemplo abaixo:
class Pessoa{ constructor(){ // Neste caso eu o renomeei com _atributo1; this._atributo1; } get atributo1(){ // Mudei aqui return this._atributo1; } set atributo1(oi){ // E aqui também this._atributo1 = oi; } }
Então, no exemplo abaixo eu fiz só renomear aonde eu chamava o atributo1, para _ atributo1, e isso é o suficiente para corrigir este problema da recursividade. Em resumo, isso é o encapsulamento no Javascript, bem simples, mas cheio de pegadinhas.
Herança
Como você já deve saber, a herança é uma das coisas mais importantes dentro de um sistema orientado a objetos, pois ela vai permitir um boa reutilização de código e bom uso de alguns padrões de projetos importantes. E ela existe dentro do Javascript, e funciona de uma forma muito intuitiva, diferente do encapsulamento. Suponhamos que eu tenha uma classe chamada Pessoa, e ela tenha os atributos nome e idade, e o método falar, como no exemplo abaixo:
class Pessoa{ constructor(nome, idade){ this.nome = nome; this.idade = idade; } falar(){ console.log("Olá mundo"); } }
Agora, digamos que eu quero ter outra classe no meu sistema que se chama Professor, ela vai ter o mesmos atributos da classe Pessoa, isso por que o professor é uma pessoa, obvio. Além desses, o professor teria outros métodos e atributos específicos deles. Contudo não seria viável eu repetir toda a estrutura da classe Pessoa na classe Professor, e para evitar essa repetição, nos podemos dizer que Professor herda todos os atributos e métodos de Pessoa, veja o exemplo abaixo de como criar uma herança entre classes no Javascript:
class Professor extends Pessoa{ constructor(nome, idade, materia){ // Chamada do construtor da classe mãe(Pessoa) // o super é uma representação do construtor da classe mãe // E sempre deve ser a primeira linha na classe filha super(nome, idade); this.materia = materia; } darAula(){ console.log("Agora vamos dar aula de " + this.materia); } }
Como você viu no exemplo acima, a herança no Javascript funciona exatamente da mesma forma das outras linguagens orientadas a objetos, ou seja, a classe filha herda tudo da classe mãe, e automaticamente já tem todos os atributos e métodos da mãe sem precisar reescrevê-los.
Em síntese isso é tudo o que você precisa saber para trabalhar com as classes no Javascript. São poucos detalhes que te ajudam a criar um código cada vez mais limpo, e com certeza esta adição a linguagem mostra o grande grau de evolução que a linguagem teve nos últimos anos.