O conceito de getters e setters é bastante comum entre as línguas populares em uso hoje em dia. Você já precisou garantir que uma determinada ação seja sempre executada antes ou depois de atribuir algum valor à propriedade de um objeto? Isto é possível com os setters. Você também pode executar qualquer tipo de processamento ao recuperar o valor de uma propriedade usando getters (por exemplo, formatação). Uma maneira de implementar getters e setters é usar métodos, como é feito em Java. Entretanto, desde ES5, temos uma alternativa muito mais elegante e prática disponível, usando a sintaxe get
e set
. Bem, vamos começar!
Em JavaScript, os objectos são colecções de propriedades e provavelmente já os usou para armazenar dados antes. Uma propriedade é simplesmente um par de valores chave. Aqui está um exemplo:
const options = {
timeout: 5000,
retries: 3,
ip: '192.168.1.20',
port: 4001,
};
Propriedades em um objeto podem ser facilmente definidas e lidas, assim:
artist.name = 'Carice Anouk van Houten';console.log(artist.name); //=> "Carice Anouk van Houten"
Again, isto definitivamente não é nada novo se você já teve alguma experiência com JavaScript. Para fazer uma distinção entre estas propriedades regulares e o outro tipo que veremos neste artigo, podemos chamar as antigas “propriedades de dados”.
Getters and setters: the old way
Before ES5, the only way had to implement getters and setters was by using regular methods. Como no exemplo abaixo:
Eu mantive a palavra-chave function no exemplo acima para deixar claro que não há mágica aqui. Apenas métodos normais. Poderíamos ter conseguido o mesmo efeito usando a sintaxe de definição de métodos do ES2015, que é mais curta e um pouco mais elegante:
const config = {
_port: 4001,
setPort(port) {
// ...
},
getPort() {
// ...
},
};
O uso destes tipos de getters e setters é bastante simples:
config.setPort(8080);
console.log(config.getPort()); //=> 8080
Incrementar um valor seria feito assim:
item.setQuantity(item.getQuantity() + 1);
Que parece realmente verboso quando comparado com incrementar uma propriedade directamente:
item.quantity += 1;
// Or even simpler...
item.quantity++;
Getters e setters: porquê?
Em linguagens OO como Java, C++ ou C#, é possível expor os membros de uma classe em diferentes níveis usando modificadores de acesso (tais como public
, protected
e private
). Isto serve para implementar um importante conceito de OOP chamado ocultação de dados.
Um uso muito comum de getters e setters nestas linguagens é expor os membros de uma classe de uma forma segura. Por exemplo, você poderia implementar apenas um getter para garantir que um membro privado nunca seria alterado de fora da classe. Um setter, por sua vez, pode ser usado para validar os valores fornecidos e preservar a integridade de instâncias dessa classe.
Como sobre JavaScript?
Em JavaScript, as coisas são um pouco diferentes. A partir do momento em que este artigo foi escrito, não há modificadores de acesso no JS. Actualmente, a melhor opção que temos é usar símbolos, que nos dão algum nível de privacidade quando usados como chaves. Algumas pessoas não gostam deles, no entanto. Especialmente porque é fácil quebrar essa privacidade usando reflection.
Não considero isso um problema real, já que idiomas que têm reflection geralmente permitem que os programadores quebrem o encapsulamento de alguma forma. O problema que vejo nos símbolos é que eles não são tão práticos de usar como um simples acessor privado seria. A boa notícia é que uma proposta EcmaScript que inclui campos privados já se encontra na fase 3. E eles serão imunes a qualquer tipo de reflexão.
No exemplo da secção anterior, não utilizámos símbolos ou qualquer outro truque para simular um membro privado. A propriedade _port
não tem nada de especial por si só. É apenas uma propriedade de dados regular e como tal, pode ser definida ou lida directamente, contornando completamente o getter e setter que criámos.
Desde que explicar símbolos e outras técnicas de encapsulamento não é o objectivo deste artigo, não implemento propriedades privadas nos seus exemplos de código. Apenas tenha em mente que getters e setters são comumente (mas nem sempre) usados para fornecer acesso aos membros encapsulados. Finalmente, note o sublinhado que precede o nome da propriedade _port
. É uma convenção de facto entre os desenvolvedores do JS, indicando que uma determinada propriedade é privada e não deve ser acessada de fora da classe onde está definida.
Getters and setters: the new way with accessor properties
Accessor properties are the modern way of defining getters and setters. Quando se trata da definição deles, eles não são muito diferentes do nosso exemplo anterior, mas é o uso deles que é realmente diferente. Primeiro, vamos adaptar nosso código para usar as propriedades dos acessores:
Como você pode ver, apenas duas coisas mudaram neste exemplo:
- Em vez de ter métodos
setPort
egetPort
. Usamos a sintaxeset
eget
juntamente com um único nome para a propriedade (“port
“, neste exemplo) - Não precisamos da palavra-chave
function
aqui. Na verdade, se você tentar usá-la, você terá uma SyntaxError.
Como usar propriedades acessoras?
Se a definição de uma propriedade acessora for bem parecida com a forma como nós criamos old school getters e setters, seu uso não tem diferença visível de como usamos propriedades de dados regulares:
config.port = 8080;
console.log(config.port); //=> 8080
E aqui está como uma expressão de incremento:
timer.seconds += 1;
Yes. É simples assim. =)
Conclusão
- Objectos são colecções de propriedades
- Uma propriedade regular é apenas um par de valores chave e pode ser chamada de “propriedade de dados”
- É possível implementar getters e setters como métodos normais. Esta é a forma “old school” de fazê-lo em JavaScript.
- Os getters e setters são normalmente usados para fornecer acesso aos membros privados de uma classe.
- As propriedades dos acessores podem comportar-se exatamente como os getters e setters baseados no método antigo, mas seu uso é projetado para fazê-los parecer propriedades de dados.
Você quer dominar o JavaScript? Eu recomendo altamente os cursos de JavaScript do Mosh.
E se você gostou deste artigo, compartilhe-o com outros também!