Como criar classes em JavaScript (ES6) e Node.js

Existe muita treta no mercado a respeito do JavaScript ser ou não ser orientado à objetos e não vou entrar nesta discussão. A ideia deste artigo é pura e simplesmente apresentar como usar classes e alguns pequenos recursos ligados à orientação à objetos disponibilizados para JS (e consequentemente Node.js) na versão 6 de 2015.

Há quem diga também que JavaScript é melhor aproveitado no paradigma funcional do que no OO, mas como um profissional que trabalhou mais de uma década com Java e C#, confesso que volta e meia me pego usando OO em aplicações Node.js e saber usar dos recursos OO disponibilizados na especificação ES6 é uma mão na roda.

Sem mais delongas, vamos ao código!

Criando classes em JavaScript

Para quem não lembra ou ainda não aprendeu, uma classe é uma especificação, um tipo novo de objeto da sua aplicação. Por exemplo, uma classe Pessoa (inicie classes sempre com letra maiúscula e no singular) irá definir propriedades e funções comuns a pessoas da sua aplicação. Assim, quando for criar pessoas usando esta classe, elas sempre possuirão a mesma estrutura.

A primeira recomendação é que você use uma classe por arquivo JS, tranformando-o em um módulo JS que deverá ser requerido/importado onde se desejar usar essa classe. Esse arquivo deve ter o mesmo nome da classe, como abaixo, onde crio uma classe Cliente.

O próximo passo é criar o construtor dessa classe, uma função especial que inicializa um objeto deste tipo, usando argumentos e processamentos internos para definir as suas propriedades.

Esse construtor acima espera nome, idade e e-mail e os utiliza para definir as propriedades homônimas de todo cliente criado a partir dessa classe. Internamente, o construtor repassa esses valores para as propriedades do objeto, iniciadas com ‘this.’. Variáveis precedidas por ‘this.’ são propriedades do objeto e serão replicadas em todas variáveis que instanciarmos como Cliente mais tarde.

Note também que ele inicializa uma propriedade dataCadastro, de maneira automática e transparente, pegando a data e hora atuais. Esse tipo de processamento pode ser realizado no construtor inclusive para validar e transformar dados passados como argumento (que tal adicionar validações usando Joi?).

O uso da palavra reservada constructor somente pode ser usada nessa função e ela é disparada automaticamente quando criamos um novo objeto Cliente usando a keyword new, como em outras linguagens orientadas a objeto (Java, C#, etc).

Para usar essa classe que criamos, usei o require no módulo Cliente.js em uma constante, e essa constante representa a classe em si. Usando o operador new, instanciei um novo cliente com nome Luiz, idade 31 e e-mail mostrado acima. Se você voltar no trecho de código anterior, onde declarei o construtor, verá que cada um desses argumentos será colocado em uma propriedade interna do cliente e isso se torna evidente quando você imprime o objeto cliente1 no console.

Objeto cliente impresso no console

Cada variável declarada como sendo um cliente tem o seu próprio conjunto de propriedades, mas a mesma estrutura básica, ficou claro?

Assim, se você declarar cliente1, cliente2, etc; cada um terá o seu nome, sua idade, etc. Independente um do outro, mas com o mesmo “esqueleto”.

Mas e os comportamentos?

Funções de classe em Javascript

Toda classe é composta de propriedades e funções. Essas funções, por uma questão de organização, devem ser sempre relativas à responsabilidade da classe em si, e geralmente manipulam ou utilizam as propriedades do objeto em questão. Assim, uma classe cliente terá funções que usam ou manipulam as propriedades do objeto cliente.

Declarar uma função de classe (chamada de método em outras linguagens OO) é feita dentro do escopo da mesma (abre e fecha chaves mais externas). Não há necessidade da palavra function tradicionalmente usada, mas o restante segue a mesma lógica de functions tradicionais.

Como sabemos se estamos manipulando uma propriedade do objeto ou uma variável comum JS? Através do uso da palavra this novamente!

No exemplo acima descrevo que os objetos do tipo/classe Cliente possuem duas funções: isAdult que retorna true/false com base na idade do objeto e outra chamada getFirstName que baseada no nome do objeto/cliente, retorna a primeira parte do mesmo.

Para chamar estas funções você primeiro deve instanciar objetos do tipo Cliente e suas execuções devem produzir retornos conforme propriedades de cada objeto em particular.

No código acima, eu instancio dois clientes com dados diferentes e depois chamo a função isAdult pra ver quais deles são adultos ou não, com base na idade informada na sua criação.

O resultado você pode ver no seu console, mas basicamente o cliente de nome Luiz é adulto, enquanto que o cliente Pedro não é.

Se você reparar bem no meu código anterior, notará que instanciei o segundo objeto sem e-mail (terceiro argumento do construtor). Isso é totalmente permitido no JavaScript, embora possa causar alguma confusão. Por isso, crie alguma lógica interna para obrigar os campos obrigatórios a serem passados, caso eles existam.

Outra sugestão, oriunda de meus tempos como “Javeiro” é que crie funções get e set para cada uma das propriedades internas das suas classes, para evitar que as mesmas sejam acessadas e manipuladas sem qualquer tipo de encapsulamento, uma vez que em JavaScript não temos modificadores de acesso como private, protected, etc. Tecnicamente chamamos isso de métodos acessores.

E por fim, note que você pode ter propriedades internas que, por sua vez, são do tipo de outra classe. Elas podem ser instanciadas com new dentro do próprio construtor, em uma variável antes ou até depois, conforme sua lógica necessitar.

Para este exemplo, considere a classe endereço abaixo:

Note que criei uma função toString() que monta uma String baseada nas propriedades do endereço. Na verdade já existe uma função toString automaticamente em todas classes JavaScript, mas estamos sobrescrevendo seu comportamento padrão através de declaração de outra função de mesmo nome. Isso é chamado de sobrescrita na orientação à objetos, uma das formas conhecidas de sobrecarga de método/função.

Agora altere a nossa classe Cliente para que eles possuam uma propriedade endereço também:

Para testar o uso de objetos Endereco como propriedade do Cliente, meu index.js vai ficar assim:

O resultado é uma frase como “Luiz mora em Tupis 125…”. Isso porque a função toString de cada objeto endereço é automaticamente chamada quando usamos eles na concatenação de Strings. Acaba acontecendo a mesma coisa que se eu chamasse dessa forma:

O uso de objetos dentro de outros objetos é o que chamamos de associação, sendo que podemos ter associações de agregação ou de composição. Bacana, não?

Propriedades e funções estáticas em JavaScript

E para encerrar este artigo, vamos falar de mais uma característica de Orientação à Objetos implementada no JavaScript: as propriedades e funções estáticas.

Um componente estático (seja ele uma função ou propriedade) é compartilhado entre todos objetos da mesma classe e não necessita que a mesma seja instanciada para que o mesmo exista e possa ser usado/manipulado. Não confundir com as constantes, pois você pode mudar uma característica estática, mas se fizer isso, vai mudar para TODOS objetos que a possuem.

Para declarar uma propriedade estática, basta declará-la com a palavra-reservada static (ao invés de var, let ou const) dentro do escopo da classe. Abaixo, dei apenas um exemplo (uso questionável nesse caso, apenas me faltou criatividade para outro exemplo, hehe).

O que muda neste caso ao invés de usar um const? O ponto principal é que essa variável é da CLASSE e não do OBJETO, para usá-la você deve chamar “classe.propriedade”. Outro ponto é que você pode mudar o valor desta propriedade em tempo de execução ao contrário de const e o último ponto é que se mudá-la, muda para a CLASSE, independente dos OBJETOS, como no exemplo abaixo.

Note que independe de existir ou não algum objeto, a propriedade idadeAdulto existe para a CLASSE e, diferente de const, PODE ser modificada. Ao usar static, você torna esta propriedade global para a classe.

E o mesmo pode ser feito com funções, como mostra a função static abaixo da classe Endereco que retorna um array de UFs existentes no Brasil.

Para usar, você também não instancia um objeto endereço e apenas chama direto classe.funcao.

Eu poderia ter usado o array resultante da função static, mas resolvi apenas imprimi-lo, acredito que você tenha entendido a ideia.

Existem muitos outros conceitos relacionados à orientação à objetos presentes na linguagem JavaScript moderna e consequentemente no Node.js, mas por ora, os conceitos mais importantes ligados a classes em JS eu apresentei neste artigo.

Espero que tenham gostado!

Curtiu o post? Então clica no banner abaixo e dá uma conferida no meu curso sobre programação web com Node.js!

Curso Node.js e MongoDB

Publicado por

Luiz Duarte

Pós-graduado em computação, professor, empreendedor, autor, Agile Coach e programador nas horas vagas.