Estou ciente de como criar getters e setters para propriedades cujos nomes já se conhecem, fazendo algo assim:
// A trivial example:
function MyObject(val){
this.count = 0;
this.value = val;
}
MyObject.prototype = {
get value(){
return this.count < 2 ? "Go away" : this._value;
},
set value(val){
this._value = val + (++this.count);
}
};
var a = new MyObject('foo');
alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"
Agora, minha pergunta é: é possível definir tipos de get-get getters e setters como esses? Ou seja, crie getters e setters para qualquer nome de propriedade que ainda não esteja definido.
O conceito é possível no PHP usando os métodos __get()
e __set()
magic (consulte a documentação do PHP para obter informações sobre estes), então estou realmente perguntando se existe um JavaScript equivalente a esses?
Escusado será dizer que, idealmente, gostaria de uma solução compatível com vários navegadores.
javascript
metaprogramming
getter-setter
daiscog
fonte
fonte
Respostas:
Atualização de 2013 e 2015 (veja abaixo a resposta original de 2011) :
Isso foi alterado conforme a especificação do ES2015 (também conhecida como "ES6"): o JavaScript agora possui proxies . Os proxies permitem criar objetos que são proxies verdadeiros para (fachadas) outros objetos. Aqui está um exemplo simples que transforma todos os valores de propriedade que são cadeias de caracteres em todos os limites na recuperação:
As operações que você não substitui têm seu comportamento padrão. Acima, tudo o que substituímos é
get
, mas há uma lista completa de operações nas quais você pode se conectar.Na
get
lista de argumentos da função de manipulador:target
é o objeto que está sendo procurado (original
, no nosso caso).name
é (é claro) o nome da propriedade que está sendo recuperada, que geralmente é uma string, mas também pode ser um símbolo.receiver
é o objeto que deve ser usado comothis
na função getter se a propriedade for um acessador e não uma propriedade de dados. No caso normal, esse é o proxy ou algo que herda dele, mas pode ser qualquer coisa, já que a armadilha pode ser acionadaReflect.get
.Isso permite que você crie um objeto com o recurso catch-all getter e setter que você deseja:
A saída do acima é:
Observe como recebemos a mensagem "inexistente" quando tentamos recuperar
foo
quando ela ainda não existe, e novamente quando a criamos, mas não depois disso.Resposta de 2011 (veja acima para atualizações de 2013 e 2015) :
Não, o JavaScript não possui um recurso de propriedade abrangente. A sintaxe do acessador que você está usando é abordada na Seção 11.1.5 da especificação e não oferece nenhum curinga ou algo parecido.
Você poderia, é claro, implementar uma função para fazê-lo, mas acho que você provavelmente não deseja usar em
f = obj.prop("foo");
vez def = obj.foo;
e emobj.prop("foo", value);
vez deobj.foo = value;
(o que seria necessário para a função manipular propriedades desconhecidas).FWIW, a função getter (eu não me incomodei com a lógica do setter) ficaria assim:
Mas, novamente, não consigo imaginar que você realmente queira fazer isso, por causa de como isso muda a maneira como você usa o objeto.
fonte
Proxy
:Object.defineProperty()
. Coloquei os detalhes na minha nova resposta .O seguinte pode ser uma abordagem original para esse problema:
Para usá-lo, as propriedades devem ser passadas como strings. Então, aqui está um exemplo de como funciona:
Edit: Uma abordagem aprimorada, mais orientada a objetos, com base no que propus é o seguinte:
Você pode vê-lo trabalhando aqui .
fonte
Prefácio:
A resposta de TJ Crowder menciona a
Proxy
, que será necessária para um get-all getter / setter para propriedades que não existem, como o OP estava pedindo. Dependendo de qual comportamento é realmente desejado com getters / setters dinâmicos, umProxy
pode não ser realmente necessário; ou, potencialmente, convém usar uma combinação de aProxy
com o que mostrarei abaixo.(PS: Eu experimentei
Proxy
minuciosamente no Firefox no Linux recentemente e achei muito capaz, mas também um pouco confuso / difícil de trabalhar e acertar. Mais importante, também achei bastante lento (pelo menos em relação a como o JavaScript otimizado tende a ser hoje em dia) - Estou falando no reino dos deca-múltiplos mais lentamente.)Para implementar especificamente getters e setters criados dinamicamente, você pode usar
Object.defineProperty()
orObject.defineProperties()
. Isso também é bastante rápido.A essência é que você pode definir um getter e / ou setter em um objeto como este:
Várias coisas a serem observadas aqui:
value
propriedade no descritor de propriedades ( não mostrado acima) simultaneamente comget
e / ouset
; dos documentos:val
propriedade fora doObject.defineProperty()
descritor de chamada / propriedade. Esse é o comportamento padrão.writable
atrue
no descritor de propriedade, se você usarget
ouset
.configurable
eenumerable
, no entanto, dependendo do que procura; dos documentos:Nesta nota, também podem ser de interesse:
Object.getOwnPropertyNames(obj)
: obtém todas as propriedades de um objeto, mesmo as não enumeráveis (AFAIK é a única maneira de fazer isso!).Object.getOwnPropertyDescriptor(obj, prop)
: obtém o descritor de propriedades de um objeto, o objeto que foi passadoObject.defineProperty()
acima.obj.propertyIsEnumerable(prop);
: para uma propriedade individual em uma instância de objeto específica, chame esta função na instância de objeto para determinar se a propriedade específica é enumerável ou não.fonte
__get
e__set
.defineProperty
não lida com esse caso. Da pergunta: "Ou seja, crie getters e setters para qualquer nome de propriedade que ainda não esteja definido." (ênfase deles).defineProperty
define propriedades com antecedência. A única maneira de fazer o que o OP pediu é um proxy.obj.whateverProperty
, de forma que a biblioteca possa interceptar isso com um getter genérico e receber o nome da propriedade usuário tentou acessar. Daí a exigência de 'get-all getters and setters'.isso funciona para mim
fonte
Function()
assim é como usareval
. Basta colocar diretamente as funções como parâmetros dedefineProperty
. Ou, se por algum motivo você insistir em criarget
e dinamicamenteset
, em seguida, usar uma função de alta ordem que cria a função e devolvê-lo, comovar get = (function(propName) { return function() { return this[propName]; };})('value');