Getters, setters e campos privados em classes
1. Introdução aos Getters e Setters em JavaScript
Getters e setters são métodos especiais que permitem controlar o acesso a propriedades de um objeto. Em classes ES6, eles são definidos com as palavras-chave get e set seguidas do nome da propriedade.
class Pessoa {
constructor(nome, idade) {
this._nome = nome;
this._idade = idade;
}
get nome() {
return this._nome;
}
set nome(novoNome) {
if (typeof novoNome !== 'string' || novoNome.trim() === '') {
throw new Error('Nome inválido');
}
this._nome = novoNome;
}
get idade() {
return this._idade;
}
set idade(novaIdade) {
if (typeof novaIdade !== 'number' || novaIdade < 0 || novaIdade > 150) {
throw new Error('Idade inválida');
}
this._idade = novaIdade;
}
}
const pessoa = new Pessoa('João', 30);
console.log(pessoa.nome); // "João"
pessoa.idade = 31; // setter valida e atualiza
// pessoa.idade = -5; // Lançaria erro: "Idade inválida"
A principal diferença entre propriedades regulares e acessadas via get/set é que getters e setters permitem executar lógica personalizada durante a leitura e escrita de valores, enquanto propriedades regulares são apenas armazenamento direto.
2. Getters e Setters: Encapsulamento e Lógica Personalizada
Getters são excelentes para computar valores sob demanda, sem armazenar dados redundantes.
class Retangulo {
constructor(largura, altura) {
this._largura = largura;
this._altura = altura;
}
get area() {
return this._largura * this._altura;
}
get perimetro() {
return 2 * (this._largura + this._altura);
}
set largura(valor) {
if (valor <= 0) throw new Error('Largura deve ser positiva');
this._largura = valor;
}
set altura(valor) {
if (valor <= 0) throw new Error('Altura deve ser positiva');
this._altura = valor;
}
}
const ret = new Retangulo(10, 5);
console.log(ret.area); // 50 (calculado na hora)
Boas práticas importantes:
- Evite efeitos colaterais inesperados em getters (eles devem ser "puros")
- Mantenha setters simples e previsíveis
- Use setters para validação, transformação de dados ou notificação de mudanças
3. Campos Privados em Classes (#)
Desde o ES2022, JavaScript suporta campos privados reais com o prefixo #. Esses campos são inacessíveis fora da classe.
class ContaBancaria {
#saldo = 0; // campo privado
constructor(titular, saldoInicial = 0) {
this.titular = titular;
this.#saldo = saldoInicial;
}
depositar(valor) {
if (valor <= 0) throw new Error('Valor deve ser positivo');
this.#saldo += valor;
}
sacar(valor) {
if (valor > this.#saldo) throw new Error('Saldo insuficiente');
this.#saldo -= valor;
}
get saldo() {
return this.#saldo;
}
}
const conta = new ContaBancaria('Maria', 1000);
conta.depositar(500);
console.log(conta.saldo); // 1500
// console.log(conta.#saldo); // SyntaxError: campo privado inacessível
Tentar acessar #saldo diretamente resulta em erro de sintaxe, garantindo encapsulamento real.
4. Combinação de Campos Privados com Getters e Setters
A combinação de campos privados com getters e setters públicos oferece o melhor dos dois mundos: privacidade real e controle de acesso.
class Usuario {
#senha;
#email;
constructor(nome, email, senha) {
this.nome = nome;
this.#email = email;
this.#senha = senha;
}
get email() {
return this.#email;
}
set email(novoEmail) {
if (!novoEmail.includes('@')) {
throw new Error('Email inválido');
}
this.#email = novoEmail;
}
verificarSenha(senha) {
return this.#senha === senha;
}
// Getter sem setter para senha (apenas verificação)
get temSenha() {
return this.#senha.length > 0;
}
}
const user = new Usuario('Ana', 'ana@email.com', '123456');
console.log(user.email); // "ana@email.com"
user.email = 'ana.novo@email.com'; // setter valida
console.log(user.email); // "ana.novo@email.com"
console.log(user.verificarSenha('123456')); // true
// user.#senha // Erro! Inacessível
5. Campos Privados vs. Convenção _ (Underscore)
Antes dos campos privados, a convenção era usar underscore (_) para indicar propriedades "privadas".
class ExemploConvencao {
constructor() {
this._privado = 'acessível'; // apenas convenção
}
}
const obj = new ExemploConvencao();
console.log(obj._privado); // Funciona! Sem proteção real
class ExemploPrivado {
#privado = 'realmente privado';
get privado() {
return this.#privado;
}
}
const obj2 = new ExemploPrivado();
// console.log(obj2.#privado); // Erro!
console.log(obj2.privado); // "realmente privado" (via getter)
Diferenças principais:
- _: apenas convenção, ainda acessível externamente
- #: privacidade real, erro ao tentar acessar fora da classe
Quando usar cada abordagem:
- Use # para dados sensíveis ou que exigem encapsulamento real
- Use _ para projetos legados ou quando precisar de compatibilidade com versões antigas do Node.js
6. Casos de Uso em Node.js e React
Node.js: Modelos de dados com validação
class Produto {
#preco;
constructor(nome, preco) {
this.nome = nome;
this.#preco = preco;
}
get preco() {
return this.#preco;
}
set preco(valor) {
if (typeof valor !== 'number' || valor <= 0) {
throw new Error('Preço deve ser um número positivo');
}
this.#preco = valor;
}
aplicarDesconto(percentual) {
if (percentual < 0 || percentual > 100) {
throw new Error('Percentual inválido');
}
this.#preco -= this.#preco * (percentual / 100);
}
}
React: Gerenciamento de estado em componentes classe
class Timer extends React.Component {
#intervalId = null;
#contador = 0;
constructor(props) {
super(props);
this.state = { segundos: 0 };
}
start() {
if (this.#intervalId) return;
this.#intervalId = setInterval(() => {
this.#contador++;
this.setState({ segundos: this.#contador });
}, 1000);
}
stop() {
if (this.#intervalId) {
clearInterval(this.#intervalId);
this.#intervalId = null;
}
}
reset() {
this.stop();
this.#contador = 0;
this.setState({ segundos: 0 });
}
render() {
return (
<div>
<p>Segundos: {this.state.segundos}</p>
<button onClick={() => this.start()}>Iniciar</button>
<button onClick={() => this.stop()}>Parar</button>
<button onClick={() => this.reset()}>Resetar</button>
</div>
);
}
componentWillUnmount() {
this.stop(); // Limpeza ao desmontar
}
}
7. Armadilhas e Boas Práticas
Getters recursivos (stack overflow)
class Problema {
get valor() {
return this.valor; // ERRO! Chamada recursiva infinita
}
}
class Solucao {
#valor;
get valor() {
return this.#valor; // Correto: acessa campo privado
}
}
Setters que não alteram o valor
class ValidadorFalho {
#dado;
set dado(valor) {
if (typeof valor !== 'string') {
// Erro comum: não altera nem lança erro
return; // Silenciosamente ignora
}
this.#dado = valor;
}
}
Sempre lance erros explícitos em setters quando a validação falhar.
Compatibilidade
Campos privados # são suportados nativamente desde Node.js 12+. Para versões anteriores, use Babel ou TypeScript com configuração adequada.
// .babelrc para transpilação
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
8. Conclusão e Comparação com Outras Abordagens
Getters e setters oferecem encapsulamento leve com lógica personalizada, enquanto campos privados (#) garantem privacidade real. Ambas as técnicas se complementam: campos privados protegem dados internos, e getters/setters controlam o acesso a esses dados.
Comparação com outras abordagens:
Object.defineProperty(): mais verboso, mas permite configuração fina (enumerable, configurable)Proxy: intercepta todas as operações do objeto, útil para metaprogramação- Campos privados
#: mais limpo e seguro para encapsulamento padrão
Dicas para projetos React modernos:
- Em componentes funcionais com hooks, use useRef para valores privados
- Em componentes classe legados, prefira campos privados # para estado interno
- Em modelos de dados (Node.js), combine campos privados com getters/setters para validação
Getters, setters e campos privados são ferramentas essenciais para escrever código JavaScript mais seguro, expressivo e bem encapsulado, tanto no backend com Node.js quanto no frontend com React.
Referências
- MDN Web Docs: Getters — Documentação oficial sobre a sintaxe de getters em JavaScript
- MDN Web Docs: Setters — Documentação oficial sobre setters e exemplos de uso
- MDN Web Docs: Private class fields — Guia completo sobre campos privados com prefixo
# - JavaScript.info: Property getters and setters — Tutorial detalhado sobre getters e setters com exemplos práticos
- Node.js Documentation: ES Modules — Documentação oficial do Node.js sobre suporte a campos privados em módulos ES
- React Documentation: Class Components — Documentação do React sobre componentes classe, onde campos privados podem ser aplicados
- Babel: Class properties plugin — Plugin Babel para transpilação de campos privados em ambientes mais antigos