Entenda a nova especificação de classes do JavaScript Harmony ES6
Há tempos a criação de classes e objetos em JavaScript tem sido complexa e difícil de entender, isso por conta de não existir uma linha clara dividindo classes de funções e objetos e, com isso, existirem diversas maneiras de se criar estes bem como diversas formas de utilizar praticas de P.O.O. como a herança.
Por estes motivos, o ECMAScript6 (também conhecido como Harmony, ES.next ou ES6) prevê uma nova especificação chamada maximally_minimal_classes, que tem como objetivo uniformizar e simplificar a definição de classes do modo mais minimalista possível.
Atualmente, o modo mais usado para se definir algo parecido com uma classe em JavaScript é utilizando uma função construtora e adicionando funções ao seu protótipo. Confira abaixo como ficaria a representação de uma Pessoa
deste modo:
var Pessoa = function(nome, email) { this.nome = nome; // verifica se o e-mail foi preenchido if (email) { this.email = email; } }; Pessoa.prototype.fala = function(){ console.log("Olá, meu nome é "+this.nome+" e meu email é "+this.email); };
Note que a sintaxe não é exatamente intuitiva, deste modo você precisaria saber o que é um prototype e que, em JavaScript, uma classe é uma função no final das contas, o que tende a confundir bastante a cabeça de desenvolvedores acostumados com P.O.O.
Agora vamos ver como ficaria uma Pessoa
utilizando a nova especificação:
class Pessoa{ constructor(nome, email){ this.nome = nome; // verifica se o e-mail foi preenchido if (email) { this.email = email; } }
fala(){ console.log("Olá, meu nome é "+this.nome+" e meu email é "+this.email); } } var leo = new Pessoa("Leonardo", "[email protected]"); leo.fala(); // Olá, meu nome é Leonardo e meu email é [email protected]
Caso você já programe em uma linguagem orientada a objetos como o Java, essa sintaxe provavelmente será muito mais intuitiva que a anterior.
Um detalhe interessante dessa abordagem é que, apesar de a função fala
ser declarada dentro da classe Pessoa
, ela será adicionada no prototype da Pessoa
, o que pode ser visivelmente mais performático do que adicioná-la diretamente na função.
Apesar de ser muito parecido com Java, essa especificação não adicionará modificadores de acesso, o que significa que todos os atributos ou métodos adicionados à classe serão públicos! Como a própria especificação diz: "private is simply achieved via private name objects".
Além da definição padrão de classes citada acima, também estará disponível uma maneira de criar pseudo-propriedades simplesmente adicionando a palavra get ou set antes do nome da função, assim como você pode fazer em objetos literais. Exemplo:
class Pessoa { constructor(nome, email) { this.nome = nome; this.comidas = \[\]; // verifica se o e-mail foi preenchido if (email) { this.email = email; } }
fala() { console.log("Olá, meu nome é "+this.nome+" e meu email é "+this.email); }
get primeiroNome() { return this.nome.split(" ")\[0\]; }
set gostaDe(comida) { this.comidas.push(comida); } }
var leo = new Pessoa("Leonardo Wolter", "[email protected]"); leo.gostaDe = "bolo"; console.log(leo.comidas); // ```"bolo"
console.log(leo.primeiroNome); // Leonardo
Agora conseguimos criar uma classe com uma sintaxe melhor, legal, mas e as heranças, como ficam?
Atualmente, o modo mais utilizado de se implementar herança é utilizando o Prototype-Chainning. Imagine que temos uma classe PessoaFisica
que herda Pessoa
. O código seria parecido com este: ```js
var PessoaFisica = function(nome, email, cpf){ Pessoa.call(this, nome, email); this.cpf = cpf; }; PessoaFisica.prototype = new Pessoa(); PessoaFisica.prototype.constructor = PessoaFisica; PessoaFisica.prototype.dizCpf = function(){ console.log(this.cpf); };
Resumindo você precisaria criar uma função construtora com o conteudo adequado, setar o prototype de `PessoaFisica` com uma instancia de `Pessoa`, consertar a propriedade construtor e, por ultimo mas nao menos importante, adicionar as funções específicas sua `PessoaFisica`.
Confira agora como implementaríamos a mesma herança utilizando a nova especificação:
```js
class PessoaFisica extends Pessoa{
constructor(nome, email, cpf){ super(nome, email); this.cpf = cpf; } dizCpf(){ console.log(this.cpf); } }
var leo = new PessoaFisica("Leonardo", "[email protected]", "meucpf" ); leo.gostaDe = "bolo"; console.log(leo.comidas); // ```"bolo"
console.log(leo.idade); // 63 leo.dizCpf(); // "meucpf" leo.fala(); // Olá, meu nome é Leonardo e meu email é [email protected]
Temos como resultado um código mais expressivo e enxuto, apenas utilizamos a palavra-chave extends
para dizer que a PessoaFisica
herda Pessoa
e substituimos a estratégia de Constructor Stealing pelo código super(nome, email)
Legal, e quando eu vou poder usar isso? Atualmente, apenas o traceur-compiler possui suporte a essa nova sintaxe, uma demonstração do código desse post funcionando pode ser vista aqui.
Caso esteja interessado nas outras funcionalidades que serão adicionadas ao Harmony, você pode conferir a tabela de compatibilidade com os browsers, compilers e node aqui.