Getters e Setters em JavaScript
Aprenda getters e setters em JavaScript: sintaxe get/set, propriedades acessoras vs dados, validação, propriedades computadas e uso em classes.
A maioria das propriedades de objetos em JavaScript são propriedades de dados — elas simplesmente armazenam um valor. Mas os objetos também suportam propriedades acessoras: propriedades sustentadas por funções que são executadas sempre que a propriedade é lida ou escrita. Essas funções são chamadas de getters e setters. Para o mundo externo, uma propriedade acessora parece uma propriedade comum (user.age), mas por trás das cenas, o seu próprio código decide o que a leitura ou atribuição dela realmente faz.
Este guia cobre a sintaxe get/set, como as propriedades acessoras diferem das propriedades de dados, os casos de uso mais comuns (validação e valores computados), como getters e setters funcionam dentro de classes, e as armadilhas que vale a pena conhecer.
Introdução a Getters e Setters de Propriedades
Um getter é uma função que é executada quando você lê uma propriedade; seu valor de retorno torna-se o valor do acesso. Um setter é uma função que é executada quando você atribui a uma propriedade; ele recebe o valor atribuído como seu único argumento. Por serem funções, eles podem executar validação, calcular um resultado dinamicamente, registrar acessos ou atualizar outras propriedades — tudo enquanto o código que os chama usa a sintaxe comum de propriedade.
Sintaxe
Dentro de um literal de objeto, defina um getter com a palavra-chave get seguida de um método, e um setter com a palavra-chave set seguida de um método que recebe um parâmetro:
let obj = {
get propName() {
// getter, the code executed when obj.propName is read
},
set propName(value) {
// setter, the code executed when obj.propName is written
}
};Você pode definir ambos, ou apenas um. Uma propriedade com apenas um getter é somente leitura; atribuir a ela não faz nada silenciosamente no modo não estrito e lança um TypeError no modo estrito. Uma propriedade com apenas um setter é somente escrita — lê-la retorna undefined.
Propriedades acessoras vs. propriedades de dados
Vale ser preciso sobre que tipo de propriedade um getter/setter cria. Uma propriedade comum como width: 5 é uma propriedade de dados com um value. Um par getter/setter, por sua vez, cria uma propriedade acessora que não tem value próprio — apenas funções get e set. Os dois são mutuamente exclusivos: um único descritor de propriedade não pode ter tanto um value quanto um get/set.
É por isso que os exemplos de validação abaixo mantêm o número real em um campo de apoio separado (_age): o acessor público age precisa de algum lugar para armazenar os dados, porque não tem um slot de valor próprio. Para inspecionar isso diretamente, veja Flags e descritores de propriedades.
Por que Usar Getters e Setters?
Getters e setters oferecem vários benefícios, incluindo:
- Encapsulamento: Eles ocultam a representação interna por trás de uma interface pública estável. Você pode alterar posteriormente como um valor é armazenado sem quebrar o código que o lê.
- Validação: Você pode rejeitar ou normalizar valores no setter antes de serem armazenados.
- Propriedades Computadas: Um getter pode derivar seu resultado de outras propriedades, de modo que o valor esteja sempre atualizado e nunca fique desatualizado.
- Compatibilidade retroativa: Se uma propriedade que costumava ser um campo simples precisar de lógica adicionada posteriormente, você pode substituí-la por um acessor com o mesmo nome — o código que a chama não muda.
Exemplos Práticos
Vamos mergulhar em alguns exemplos práticos para ilustrar como getters e setters podem ser usados em cenários do mundo real.
Exemplo 1: Objeto de Usuário com Validação de Idade
Considere um objeto de usuário onde queremos garantir que a propriedade age esteja sempre dentro de um intervalo razoável. O setter valida a entrada; o getter simplesmente retorna o campo de apoio.
Exemplo 2: Criando Propriedades Computadas
Getters nos permitem criar propriedades que são calculadas dinamicamente com base em outros dados. Cada vez que area é lida, ela é recalculada, de modo que permanece sincronizada se width ou height mudar.
Exemplo 3: Definindo acessores com Object.defineProperty
A sintaxe literal get/set é a maneira mais comum de declarar acessores, mas você também pode adicioná-los a um objeto existente — incluindo após sua criação — com Object.defineProperty. Isso é útil quando o nome da propriedade é dinâmico ou quando você quer controlar flags como enumerable.
Boas Práticas
Ao usar getters e setters, considere as seguintes boas práticas para garantir que seu código seja limpo, sustentável e eficiente:
- Evite Efeitos Colaterais em Getters: Getters devem ser rápidos e livres de efeitos colaterais, pois são frequentemente chamados implicitamente pelo motor ou durante a enumeração de propriedades.
- Validação: Sempre valide os dados nos setters para evitar que dados inválidos ou prejudiciais sejam armazenados.
- Convenções de Nomenclatura: Use um underscore inicial (
_) para os nomes de propriedades de apoio para indicar que são privadas. - Serialização JSON: Note que
JSON.stringify()ignora getters por padrão. Use uma função replacer ou serialização explícita se precisar incluir valores computados.
Casos de Uso Avançados
Nomes de Propriedades Dinâmicos
O JavaScript ES6 introduziu nomes de propriedades computados, que podem ser combinados com getters e setters para definir dinamicamente chaves acessoras com base em variáveis ou expressões.
Integração com Classes
Getters e setters também são muito úteis na programação baseada em classes, oferecendo uma maneira de encapsular e controlar o acesso às propriedades de classe. A sintaxe é idêntica — basta escrever métodos get/set no corpo da classe — e eles são colocados no protótipo, de modo que cada instância os compartilha.
Armadilhas Comuns
Alguns erros pegam os desenvolvedores de surpresa:
- Recursão infinita por campo de apoio incorreto. Se um setter para
nameatribuir athis.nameem vez de um campo separado comothis._name, a atribuição aciona o setter novamente, indefinidamente. Sempre armazene o valor sob uma chave diferente. - Um getter sem setter é somente leitura. Atribuir a ele não faz nada (ou lança no modo estrito), o que pode parecer um bug silencioso.
JSON.stringify()chama getters mas ignora setters. Um getter computado é serializado (seu valor de retorno é incluído), mas não há como restaurá-lo por meio de um setter emJSON.parse()— você só recupera dados simples.thissegue o local da chamada. Dentro de um getter ou setter,thisé o objeto em que a propriedade foi acessada. Se você copiar o acessor para outro objeto,thismudará de acordo — veja Métodos de objeto e "this".
O exemplo abaixo mostra a armadilha de recursão e sua correção:
Conclusão
Dominar getters e setters de propriedades é um passo crucial para se tornar um desenvolvedor JavaScript proficiente. Esses recursos não apenas melhoram a funcionalidade e a segurança do seu código, mas também abrem caminho para bases de código mais legíveis e sustentáveis. Seguindo as boas práticas e os exemplos fornecidos neste guia, os desenvolvedores podem utilizar efetivamente getters e setters a seu favor, resultando em aplicações JavaScript mais robustas e eficientes.