Dezembro 23, 2013
Para a maior parte, em JavaScript, o que você vê é o que você recebe. Um valor é um valor; não há truques. Às vezes, porém, você quer um valor baseado em alguns outros valores: o nome completo de alguém, por exemplo, é uma concatenação do seu primeiro e último nome. Se você tem um objeto person
, e quer que os usuários desse objeto possam definir o nome completo, primeiro ou último nome, e ver essa mudança imediatamente refletida nos outros valores, você o construiria convencionalmente com funções:
person.setLastName('Smith');person.setFirstName('Jimmy');person.getFullName(); // Jimmy Smith
Mas isso é feio, e requer que os usuários do seu objeto se preocupem que as propriedades estejam relacionadas; em um exemplo mais complexo, isso pode não ser tão óbvio quanto com nomes. Felizmente, há uma maneira melhor, adicionada em ECMAScript 5.
Meet getters and setters.
How
Let’s make that person object. Queremos ser capazes de definir o primeiro nome, sobrenome ou nome completo, e fazer com que atualize os outros dois automaticamente.
var person = { firstName: 'Jimmy', lastName: 'Smith', get fullName() { return this.firstName + ' ' + this.lastName; }, set fullName (name) { var words = name.toString().split(' '); this.firstName = words || ''; this.lastName = words || ''; }}person.fullName = 'Jack Franklin';console.log(person.firstName); // Jackconsole.log(person.lastName) // Franklin
Então o que está acontecendo aqui?
As palavras-chave get e setters são importantes. Seguindo-as está a propriedade a que se referem (fullName
) e um corpo de função que define o comportamento quando a propriedade é acedida (name = person.fullName
) ou modificada (person.fullName = 'Some Name'
).
Estas duas palavras-chave definem funções de acessor: um getter e um setter para a propriedade fullName
. Quando a propriedade é acessada, o valor de retorno do getter é usado. Quando um valor é definido, o setter é chamado e passa o valor que foi definido. Cabe a você o que você faz com esse valor, mas o que é retornado do setter é o valor que foi passado – então você não precisa retornar nada.
O jeito oficial: Object.defineProperty
Embora o método em linha de declarar getters e setters, também pode ser feito mais explicitamente via Object.defineProperty
(Documentação MDN). Este método leva três argumentos. O primeiro é o objeto para adicionar a propriedade, o segundo é o nome da propriedade, e o terceiro é um objeto que descreve a propriedade (conhecido como o descritor da propriedade). Aqui está um exemplo que replica o exemplo acima:
var person = { firstName: 'Jimmy', lastName: 'Smith'};Object.defineProperty(person, 'fullName', {get: function() {return firstName + ' ' + lastName;},set: function(name) {var words = name.split(' ');this.firstName = words || '';this.lastName = words || '';}});
A vantagem aqui não é imediatamente aparente. Para além de poder adicionar propriedades após criar o objecto inicial, existe algum benefício real?
Quando se define uma propriedade desta forma, pode-se fazer muito mais do que apenas definir um setter ou getter. Você também pode passar as seguintes chaves:
-
configurable
(false
por padrão): se isso for verdade, a configuração da propriedade será modificável no futuro. -
enumerable
(false
por padrão): se for verdade, a propriedade aparecerá ao passar por cima do objeto (for (var key in obj)
).
Também podemos definir propriedades que não tenham getters ou setters explícitos:
Object.defineProperty(person, 'age', { value: 42});
Isso criará person.age
, e a definirá para o valor 42. É importante notar que esta propriedade não é escrevível. A chamada person.age = 99
não terá efeito. Desta forma, você pode criar propriedades somente leitura. Se uma propriedade tem um conjunto de chaves value
, ela não pode ter um getter ou setter. Propriedades podem ter valores ou acessores, não ambos.
Não só isso, mas como a propriedade enumerable
tem como padrão false
, esta propriedade não aparecerá quando passarmos por cima das chaves do objeto.
Se quiséssemos tornar uma propriedade escrevível, precisaríamos definir a propriedade writable
> propriedade:
Object.defineProperty(person, 'age', { value: 42, writable: true});
Agora, person.age = 99;
terá o efeito desejado.
Overso uso
Lembrar: só porque existe uma propriedade, ela não precisa ser usada o tempo todo. Getters e Setters têm seus casos de uso, mas não exagere, ou muito provavelmente você acabará com um design que é confuso para aqueles que interagem com seus objetos. Usados com cuidado, eles são muito poderosos. Mas com grande poder vem uma grande responsabilidade.
Suporte de navegador?
IE9 e superiores têm suporte total para Object.defineProperty
, juntamente com Safari 5+, Firefox 4+, Chrome 5+ e Opera 12+. Se você estiver trabalhando com o Node.js, há suporte total. Você não adora o Node?!
Este artigo foi escrito em co-autoria com Tom Ashworth. Obrigado ao Tom por toda a sua ajuda para montar isto.
Se você gostou deste post, junte-se à newsletter para obter atualizações sobre novos conteúdos 😎.
>
Todos os posts do blog
Se você gostou deste post, não deixe de me seguir no Twitter.