Classes ES6: sintaxe e herança
1. Introdução às Classes ES6
Antes do ES6, JavaScript utilizava funções construtoras e protótipos para simular orientação a objetos. Com a especificação ECMAScript 2015 (ES6), as classes foram introduzidas como syntactic sugar sobre o sistema de protótipos existente. Isso significa que, por baixo dos panos, continuamos trabalhando com protótipos, mas a sintaxe se tornou mais limpa e familiar para desenvolvedores de linguagens como Java ou C++.
// Função construtora tradicional (pré-ES6)
function Pessoa(nome) {
this.nome = nome;
}
Pessoa.prototype.saudacao = function() {
return `Olá, eu sou ${this.nome}`;
};
// Classe ES6 equivalente
class Pessoa {
constructor(nome) {
this.nome = nome;
}
saudacao() {
return `Olá, eu sou ${this.nome}`;
}
}
Um ponto crucial: classes em JavaScript operam em strict mode automaticamente, mesmo sem a diretiva "use strict". Além disso, diferentemente de funções construtoras, classes não sofrem hoisting — você não pode usar uma classe antes de declará-la.
2. Sintaxe de Classes: Métodos e Propriedades
As classes ES6 permitem definir métodos de instância e métodos estáticos de forma clara. Métodos estáticos são chamados diretamente na classe, não em instâncias.
class Matematica {
static somar(a, b) {
return a + b;
}
static PI = 3.14159; // Campo estático (ES2022)
constructor(valor) {
this.valor = valor;
}
// Getter
get valorDobrado() {
return this.valor * 2;
}
// Setter
set valorDobrado(novoValor) {
this.valor = novoValor / 2;
}
}
console.log(Matematica.somar(2, 3)); // 5
const m = new Matematica(10);
console.log(m.valorDobrado); // 20
Desde o ES2022, podemos declarar campos públicos diretamente no corpo da classe, sem necessidade do construtor:
class Usuario {
nome = 'Anônimo'; // Campo público
#senha = ''; // Campo privado (veremos adiante)
constructor(nome, senha) {
this.nome = nome || this.nome;
this.#senha = senha;
}
}
3. O Construtor e a Criação de Instâncias
O método constructor é especial — ele é executado automaticamente quando usamos new. É o local ideal para inicializar propriedades da instância.
class Animal {
constructor(nome, especie) {
this.nome = nome;
this.especie = especie;
this.vivo = true;
}
}
const gato = new Animal('Mimi', 'felino');
Em subclasses, o construtor deve chamar super() antes de usar this. Se você não definir um construtor na subclasse, o JavaScript cria um automaticamente que chama super(...args).
class Mamifero extends Animal {
constructor(nome, especie, temPelos) {
super(nome, especie); // Obrigatório!
this.temPelos = temPelos;
}
}
Um detalhe importante: se o construtor retornar explicitamente um objeto, esse objeto será usado como resultado de new, ignorando a instância criada automaticamente. Isso é raro, mas possível.
4. Herança com extends
A palavra-chave extends estabelece uma cadeia de protótipos entre classes. A classe filha herda todos os métodos e propriedades da classe pai.
class Veiculo {
constructor(marca, ano) {
this.marca = marca;
this.ano = ano;
}
descricao() {
return `${this.marca} (${this.ano})`;
}
}
class Carro extends Veiculo {
constructor(marca, ano, portas) {
super(marca, ano);
this.portas = portas;
}
// Override do método
descricao() {
return `${super.descricao()} - ${this.portas} portas`;
}
buzinar() {
return 'Biiii!';
}
}
const fusca = new Carro('Volkswagen', 1970, 2);
console.log(fusca.descricao()); // Volkswagen (1970) - 2 portas
console.log(fusca.buzinar()); // Biiii!
Note o uso de super.descricao() para acessar o método da classe pai dentro do método sobrescrito.
5. Mixins e Herança Múltipla Simulada
JavaScript não suporta herança múltipla diretamente. Uma classe só pode estender uma única classe base. No entanto, podemos simular esse comportamento com mixins — funções que combinam comportamentos de múltiplas fontes.
// Mixins como funções auxiliares
const Navegavel = {
navegar() {
return 'Navegando...';
}
};
const Flutuavel = {
flutuar() {
return 'Flutuando...';
}
};
// Função que aplica mixins a uma classe
function aplicarMixins(targetClass, ...mixins) {
Object.assign(targetClass.prototype, ...mixins);
}
class Barco {
constructor(nome) {
this.nome = nome;
}
}
aplicarMixins(Barco, Navegavel, Flutuavel);
const barco = new Barco('Titanic');
console.log(barco.navegar()); // Navegando...
console.log(barco.flutuar()); // Flutuando...
Em React, antes dos hooks, era comum usar mixins com React.createClass. Hoje, com componentes de classe, prefere-se composição ou Higher-Order Components (HOCs).
6. Campos Privados e Encapsulamento
O ES2022 introduziu campos privados reais usando o prefixo #. Diferente da convenção _ (que era apenas uma sugestão), campos com # são verdadeiramente privados — inacessíveis fora da classe.
class ContaBancaria {
#saldo = 0; // Campo privado
constructor(titular, saldoInicial) {
this.titular = titular;
this.#saldo = saldoInicial;
}
#validarSaque(valor) { // Método privado
return valor <= this.#saldo;
}
depositar(valor) {
if (valor > 0) {
this.#saldo += valor;
}
}
sacar(valor) {
if (this.#validarSaque(valor)) {
this.#saldo -= valor;
return true;
}
return false;
}
get saldo() {
return this.#saldo;
}
}
const conta = new ContaBancaria('João', 1000);
console.log(conta.saldo); // 1000 (via getter)
// console.log(conta.#saldo); // Erro! Propriedade privada
A convenção _ (ex.: _saldo) ainda é amplamente usada, mas não oferece proteção real — apenas sinaliza que a propriedade não deveria ser acessada externamente.
7. Classes no Ecossistema Node.js e React
Em Node.js, classes são comumente exportadas como módulos:
// utils/Validador.js
export class Validador {
static email(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
static cpf(cpf) {
// Lógica de validação...
return true;
}
}
// app.js
import { Validador } from './utils/Validador.js';
console.log(Validador.email('teste@exemplo.com')); // true
Em React, componentes de classe ainda são suportados e utilizam métodos de ciclo de vida:
import React, { Component } from 'react';
class Contador extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
componentDidMount() {
console.log('Componente montado!');
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.count !== this.state.count;
}
render() {
return (
<div>
<p>Contagem: {this.state.count}</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Incrementar
</button>
</div>
);
}
}
Apesar dos hooks terem se tornado o padrão no React moderno, componentes de classe ainda são amplamente encontrados em bases de código legadas e em situações que exigem métodos de ciclo de vida específicos.
8. Boas Práticas e Armadilhas Comuns
Não esquecer o new: Chamar uma classe sem new lança um TypeError.
const p = Pessoa('João'); // TypeError: Class constructor Pessoa cannot be invoked without 'new'
Hoisting: Ao contrário de funções, classes não são içadas:
// Isto funciona:
const a = soma(2, 3);
function soma(a, b) { return a + b; }
// Isto NÃO funciona:
const obj = new MinhaClasse(); // ReferenceError
class MinhaClasse {}
Perda de contexto this: Em callbacks, o this pode se perder. Use arrow functions ou .bind():
class Botao {
constructor(texto) {
this.texto = texto;
}
clicar() {
console.log(`Clicou em: ${this.texto}`);
}
// Solução 1: arrow function
clicarArrow = () => {
console.log(`Clicou em: ${this.texto}`);
}
}
const btn = new Botao('Enviar');
document.getElementById('meuBotao').addEventListener('click', btn.clicar); // this = undefined
document.getElementById('meuBotao').addEventListener('click', btn.clicarArrow); // Correto
Preferência por classes vs funções em React: Embora componentes de classe ainda funcionem, o ecossistema React moderno favorece componentes funcionais com hooks. Use classes apenas quando houver necessidade explícita de métodos de ciclo de vida ou quando mantendo código legado.
Referências
- MDN Web Docs: Classes em JavaScript — Documentação oficial completa sobre sintaxe de classes, herança, métodos estáticos e privados.
- JavaScript.info: Class inheritance — Tutorial detalhado sobre herança com
extends,supere substituição de métodos. - Node.js Documentation: ES Modules — Guia oficial sobre exportação e importação de classes em módulos Node.js.
- React Documentation: Class Components — Documentação oficial do React sobre componentes de classe, ciclo de vida e estado.
- TC39 Proposal: Private class fields — Proposta oficial que introduziu campos privados com
#e campos públicos no ES2022. - 2ality Blog: ES6 Classes in Depth — Artigo técnico profundo do Dr. Axel Rauschmayer sobre classes ES6, herança e diferenças com protótipos.