Classes em JavaScript
Esta aula aborda as classes em JavaScript, uma sintaxe que simplifica a criação de objetos e a herança. Você aprenderá a sintaxe básica, o uso do construtor, como definir métodos, campos de classe e a importância da palavra-chave this. O conteúdo inclui exemplos práticos, boas práticas e exercícios para fixação.
As classes em JavaScript foram introduzidas no ECMAScript 2015 (ES6) como uma forma mais clara e orientada a objetos de trabalhar com herança e protótipos. Elas não introduzem um novo modelo de herança, mas fornecem uma sintaxe mais familiar para programadores vindos de outras linguagens como Java ou C++. Nesta aula, vamos explorar os componentes essenciais de uma classe: sintaxe, construtor, métodos, campos e a palavra-chave this.
Com as classes, você pode organizar seu código de maneira mais modular, reutilizável e legível, especialmente em aplicações de médio e grande porte. Vamos ver cada parte em detalhes, com exemplos práticos que você pode executar em seu ambiente de desenvolvimento.
Sintaxe
A sintaxe de uma classe em JavaScript é declarada com a palavra-chave class, seguida do nome da classe (geralmente iniciando com letra maiúscula, por convenção) e um bloco de chaves {}. Dentro desse bloco, definimos os membros da classe: construtor, métodos e campos.
Exemplo básico de declaração de classe:
class Pessoa {
// corpo da classe
}Diferente de funções construtoras, as classes não são hoisted (elevadas). Ou seja, você não pode usar uma classe antes de declará-la no código. Isso evita erros de referência e torna o código mais previsível.
Podemos também utilizar expressões de classe, atribuindo a classe a uma variável:
const Animal = class {
// corpo da classe
};Essa forma é útil quando você precisa criar classes dinamicamente ou passá-las como argumento para funções.
Construtor
O construtor é um método especial chamado automaticamente quando uma nova instância da classe é criada com a palavra-chave new. Ele é usado para inicializar as propriedades do objeto. O método construtor é definido com o nome constructor.
Exemplo:
class Pessoa {
constructor(nome, idade) {
this.nome = nome;
this.idade = idade;
}
}
const pessoa1 = new Pessoa('Ana', 30);
console.log(pessoa1.nome); // Ana
console.log(pessoa1.idade); // 30Se você não definir um construtor, a classe terá um construtor padrão que simplesmente cria o objeto sem inicializar propriedades. Você pode ter apenas um construtor por classe, mas pode definir valores padrão para os parâmetros, como em qualquer função.
class Produto {
constructor(nome = 'Genérico', preco = 0) {
this.nome = nome;
this.preco = preco;
}
}
const p1 = new Produto();
console.log(p1.nome); // Genérico
console.log(p1.preco); // 0Métodos
Métodos são funções definidas dentro da classe que descrevem o comportamento das instâncias. Eles são definidos sem a palavra-chave function e são adicionados ao protótipo da classe, o que significa que todas as instâncias compartilham o mesmo método, economizando memória.
Exemplo de método:
class Calculadora {
somar(a, b) {
return a + b;
}
subtrair(a, b) {
return a - b;
}
}
const calc = new Calculadora();
console.log(calc.somar(5, 3)); // 8
console.log(calc.subtrair(5, 3)); // 2Além dos métodos de instância, você pode definir métodos estáticos com a palavra-chave static. Esses métodos são chamados diretamente na classe, não em instâncias. Eles são úteis para funções utilitárias relacionadas à classe.
class Matematica {
static dobrar(numero) {
return numero * 2;
}
}
console.log(Matematica.dobrar(4)); // 8Também é possível definir getters e setters usando get e set para controlar o acesso a propriedades.
class Temperatura {
constructor(celsius) {
this._celsius = celsius;
}
get celsius() {
return this._celsius;
}
set celsius(valor) {
if (valor < -273.15) {
throw new Error('Temperatura abaixo do zero absoluto');
}
this._celsius = valor;
}
}
const temp = new Temperatura(25);
console.log(temp.celsius); // 25
temp.celsius = 30;
console.log(temp.celsius); // 30Campos
Campos (ou propriedades de classe) são variáveis declaradas diretamente na classe, que podem ter valores iniciais. Antes do ES2022, era comum declarar campos apenas no construtor. Agora, podemos declará-los no corpo da classe, tornando o código mais explícito.
Exemplo:
class Contador {
valor = 0; // campo público
static total = 0; // campo estático
incrementar() {
this.valor++;
Contador.total++;
}
}
const c1 = new Contador();
c1.incrementar();
c1.incrementar();
console.log(c1.valor); // 2
console.log(Contador.total); // 2Além dos campos públicos, temos campos privados, que são precedidos por #. Eles são acessíveis apenas dentro da classe, garantindo encapsulamento real.
class Pessoa {
#nome; // campo privado
constructor(nome) {
this.#nome = nome;
}
getNome() {
return this.#nome;
}
}
const pessoa = new Pessoa('João');
console.log(pessoa.getNome()); // João
// console.log(pessoa.#nome); // Erro: SyntaxErrorOs campos privados são uma adição recente (ES2022) e são suportados na maioria dos navegadores modernos e no Node.js.
this
A palavra-chave this é um dos conceitos mais confusos em JavaScript. Dentro de uma classe, this refere-se à instância atual do objeto. Ele é usado para acessar propriedades e métodos da própria instância.
Exemplo:
class Cachorro {
constructor(nome) {
this.nome = nome;
}
latir() {
console.log(`${this.nome} está latindo!`);
}
}
const rex = new Cachorro('Rex');
rex.latir(); // Rex está latindo!No entanto, this pode mudar de contexto dependendo de como a função é chamada. Por exemplo, se você passar um método como callback, o this pode ser perdido. Para contornar isso, você pode usar funções de seta (arrow functions) ou o método bind.
class Botao {
constructor() {
this.clicado = 0;
}
clicar() {
this.clicado++;
console.log(`Clicado ${this.clicado} vezes`);
}
}
const botao = new Botao();
// Perda de contexto se passarmos como callback
setTimeout(botao.clicar, 1000); // erro: this é undefined
// Solução: usar arrow function no construtor ou bind
class Botao2 {
constructor() {
this.clicado = 0;
this.clicar = this.clicar.bind(this);
}
clicar() {
this.clicado++;
console.log(`Clicado ${this.clicado} vezes`);
}
}
const botao2 = new Botao2();
setTimeout(botao2.clicar, 1000); // funcionaUma alternativa moderna é usar campos de classe com arrow functions, que capturam o this do escopo onde foram definidos.
class Botao3 {
clicado = 0;
clicar = () => {
this.clicado++;
console.log(`Clicado ${this.clicado} vezes`);
};
}
const botao3 = new Botao3();
setTimeout(botao3.clicar, 1000); // funcionaEntender this é crucial para evitar bugs sutis em seu código.
Boas Práticas
- Use nomes de classes em PascalCase.
- Declare campos no topo da classe para facilitar a leitura.
- Prefira campos privados para dados sensíveis.
- Evite métodos grandes; divida em métodos menores.
- Use métodos estáticos para funções que não dependem de instância.
- Use getters/setters para validar dados.
Referências
- MDN: Classes
- MDN: Construtor
- MDN: Métodos estáticos
- MDN: Campos privados
- MDN: this
- JavaScript.info: Classes
- W3Schools: Classes
Exercícios
Crie uma classe chamada
Livrocom construtor que recebatituloeautor. Adicione um métodoexibirInfo()que retorna uma string com o título e autor.✓ Resposta:class Livro { constructor(titulo, autor) { this.titulo = titulo; this.autor = autor; } exibirInfo() { return `${this.titulo} - ${this.autor}`; } } const livro = new Livro('O Senhor dos Anéis', 'J.R.R. Tolkien'); console.log(livro.exibirInfo()); // O Senhor dos Anéis - J.R.R. TolkienCrie uma classe
ContaBancariacom campos privados#saldoe métodosdepositar(valor)esacar(valor), que atualizam o saldo. Adicione um getter parasaldo.✓ Resposta:class ContaBancaria { #saldo = 0; depositar(valor) { if (valor > 0) { this.#saldo += valor; } } sacar(valor) { if (valor > 0 && valor <= this.#saldo) { this.#saldo -= valor; } } get saldo() { return this.#saldo; } } const conta = new ContaBancaria(); conta.depositar(100); conta.sacar(30); console.log(conta.saldo); // 70Escreva uma classe
Pessoacom um método estáticocriarque recebe nome e idade e retorna uma nova instância. Além disso, adicione um método de instânciaapresentar()que exibe uma mensagem.✓ Resposta:class Pessoa { constructor(nome, idade) { this.nome = nome; this.idade = idade; } static criar(nome, idade) { return new Pessoa(nome, idade); } apresentar() { console.log(`Olá, meu nome é ${this.nome} e tenho ${this.idade} anos.`); } } const pessoa = Pessoa.criar('Maria', 25); pessoa.apresentar(); // Olá, meu nome é Maria e tenho 25 anos.Explique o que acontece com o
thisquando você passa um método de uma classe como callback e como resolver esse problema usando arrow function. Dê um exemplo.✓ Resposta:Quando passamos um método como callback, ele perde o contexto do objeto, então
thisse tornaundefined(em modo estrito) ou o objeto global. Para resolver, podemos usar uma arrow function que captura othisdo escopo onde foi definida, ou usar.bind().class Relogio { constructor() { this.hora = 0; // arrow function como método de classe this.tick = () => { this.hora++; console.log(this.hora); }; } } const relogio = new Relogio(); setInterval(relogio.tick, 1000); // funciona, this não é perdidoCrie uma classe
Carrocom um campo públicomarcae um campo privado#velocidadeinicializado em 0. Adicione métodosacelerar()efrear()que alteram a velocidade, e um getter paravelocidade.✓ Resposta:class Carro { marca; #velocidade = 0; constructor(marca) { this.marca = marca; } acelerar() { this.#velocidade += 10; } frear() { if (this.#velocidade >= 10) { this.#velocidade -= 10; } } get velocidade() { return this.#velocidade; } } const carro = new Carro('Toyota'); carro.acelerar(); carro.acelerar(); carro.frear(); console.log(carro.velocidade); // 10