Soy consciente de cómo crear captadores y establecedores para propiedades cuyos nombres ya se conocen, haciendo algo como esto:
// 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"
Ahora, mi pregunta es, ¿es posible definir una especie de captadores y establecedores generales como estos? Es decir, crear captadores y establecedores para cualquier nombre de propiedad que no esté definido.
El concepto es posible en PHP utilizando los métodos __get()
y __set()
magic (consulte la documentación de PHP para obtener información sobre estos), por lo que realmente pregunto si hay un equivalente de JavaScript para estos.
No hace falta decir que, idealmente, me gustaría una solución que sea compatible con varios navegadores.
javascript
metaprogramming
getter-setter
daiscog
fuente
fuente
Respuestas:
Actualización de 2013 y 2015 (ver abajo la respuesta original de 2011) :
Esto cambió a partir de la especificación ES2015 (también conocido como "ES6"): JavaScript ahora tiene proxies . Los servidores proxy le permiten crear objetos que son servidores proxy verdaderos para (fachadas) de otros objetos. Aquí hay un ejemplo simple que convierte los valores de propiedad que son cadenas en mayúsculas en la recuperación:
Las operaciones que no anula tienen su comportamiento predeterminado. En lo anterior, todo lo que anulamos es
get
, pero hay una lista completa de operaciones en las que puede engancharse.En la
get
lista de argumentos de la función del controlador:target
es el objeto que se representa (original
en nuestro caso).name
es (por supuesto) el nombre de la propiedad que se está recuperando, que generalmente es una cadena pero también podría ser un Símbolo.receiver
es el objeto que debe usarse comothis
en la función getter si la propiedad es un descriptor de acceso en lugar de una propiedad de datos. En el caso normal, este es el proxy o algo que hereda de él, pero puede ser cualquier cosa ya que la trampa puede ser activada porReflect.get
.Esto le permite crear un objeto con la función de captador y configurador general que desea:
La salida de lo anterior es:
Observe cómo obtenemos el mensaje "inexistente" cuando intentamos recuperarlo
foo
cuando aún no existe, y nuevamente cuando lo creamos, pero no después de eso.Respuesta de 2011 (ver arriba para actualizaciones de 2013 y 2015) :
No, JavaScript no tiene una característica de propiedad general. La sintaxis del descriptor de acceso que está utilizando se trata en la Sección 11.1.5 de la especificación, y no ofrece ningún comodín o algo así.
Por supuesto, podría implementar una función para hacerlo, pero supongo que probablemente no quiera usar en
f = obj.prop("foo");
lugar def = obj.foo;
y enobj.prop("foo", value);
lugar deobj.foo = value;
(lo que sería necesario para que la función maneje propiedades desconocidas).FWIW, la función getter (no me molesté con la lógica del setter) se vería así:
Pero nuevamente, no puedo imaginar que realmente quieras hacer eso, debido a cómo cambia la forma en que usas el objeto.
fuente
Proxy
:Object.defineProperty()
. Puse los detalles en mi nueva respuesta .Lo siguiente podría ser un enfoque original para este problema:
Para usarlo, las propiedades deben pasarse como cadenas. Aquí hay un ejemplo de cómo funciona:
Editar: un enfoque mejorado y más orientado a objetos basado en lo que propuse es el siguiente:
Puedes verlo trabajando aquí .
fuente
Prefacio:
La respuesta de TJ Crowder menciona un
Proxy
, que será necesario para un captador / configurador general para propiedades que no existen, como lo pedía el OP. Dependiendo de qué comportamiento se quiera realmente con getters / setters dinámicos, aProxy
puede no ser realmente necesario; o, potencialmente, es posible que desee utilizar una combinación de aProxy
con lo que le mostraré a continuación.(PD: He experimentado con
Proxy
Firefox en Linux recientemente y he encontrado que es muy capaz, pero también algo confuso / difícil de trabajar y hacerlo bien. Más importante, también he encontrado que es bastante lento (al menos en relación con lo optimizado que tiende a ser JavaScript hoy en día): estoy hablando en el ámbito de los deca-múltiplos más lento).Para implementar getters y setters creados dinámicamente específicamente, puede usar
Object.defineProperty()
oObject.defineProperties()
. Esto también es bastante rápido.La esencia es que puede definir un captador y / o definidor en un objeto de la siguiente manera:
Varias cosas a tener en cuenta aquí:
value
propiedad en el descriptor de propiedad ( no se muestra arriba) simultáneamente conget
y / oset
; de los documentos:val
propiedad fuera delObject.defineProperty()
descriptor de llamada / propiedad. Este es un comportamiento estándar.writable
atrue
en el descriptor de propiedad si se utilizaget
oset
.configurable
yenumerable
, sin embargo, dependiendo de lo que busca; de los documentos:En esta nota, estos también pueden ser de interés:
Object.getOwnPropertyNames(obj)
: obtiene todas las propiedades de un objeto, incluso las que no se pueden enumerar (¡ASÍ QUE esta es la única forma de hacerlo!).Object.getOwnPropertyDescriptor(obj, prop)
: obtiene el descriptor de propiedad de un objeto, el objeto que se pasóObject.defineProperty()
arriba.obj.propertyIsEnumerable(prop);
: para una propiedad individual en una instancia de objeto específica, llame a esta función en la instancia de objeto para determinar si la propiedad específica es enumerable o no.fuente
__get
y__set
.defineProperty
no maneja ese caso. De la pregunta: "Es decir, crear captadores y definidores para cualquier nombre de propiedad que no esté definido". (Su énfasis).defineProperty
define propiedades de antemano. La única forma de hacer lo que solicitó el OP es un proxy.obj.whateverProperty
tal manera que la biblioteca pueda interceptar eso con un captador genérico y recibir el nombre de la propiedad. El usuario intentó acceder. De ahí el requisito de 'captadores y establecedores generales'.esto funciona para mi
fuente
Function()
así es como usareval
. Simplemente ponga directamente las funciones como parámetros dedefineProperty
. O, si por alguna razón insiste en crear dinámicamenteget
yset
, a continuación, utilice una función de alto orden que crea la función y la devuelve, comovar get = (function(propName) { return function() { return this[propName]; };})('value');