setters recorrer objetos objeto metodos lista getters convertir buscar array and javascript metaprogramming getter-setter

recorrer - ¿Es posible implementar getters/setters dinámicos en JavaScript?



metodos en javascript (4)

En el Javascript moderno (FF4 +, IE9 +, Chrome 5+, Safari 5.1+, Opera 11.60+), existe Object.defineProperty . Este ejemplo en el MDN explica muy bien cómo funciona defineProperty , y hace getters dinámicos y setters posibles.

Técnicamente, esto no funcionará en ninguna consulta dinámica como la que estás buscando, pero si tus getters y setters válidos están definidos por, digamos, una llamada AJAX a un servidor JSON-RPC, por ejemplo, entonces podrías usar esto en de la siguiente manera:

arrayOfNewProperties.forEach(function(property) { Object.defineProperty(myObject, property.name, { set: property.setter, get: property.getter }); });

Soy consciente de cómo crear getters y setters para propiedades cuyos nombres uno ya conoce, 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 atrapadores y establecedores como estos? Es decir, crea getters y setters para cualquier nombre de propiedad que no esté ya definido.

El concepto es posible en PHP usando los métodos mágicos __get() y __set() (consulte la documentación de PHP para obtener información sobre estos), así que realmente estoy preguntando ¿existe un JavaScript equivalente a estos?

Huelga decir que idealmente me gustaría una solución compatible con varios navegadores.


Lo siguiente podría ser un enfoque original a este problema:

var obj = { emptyValue: null, get: function(prop){ if(typeof this[prop] == "undefined") return this.emptyValue; else return this[prop]; }, set: function(prop,value){ this[prop] = value; } }

Para usarlo, las propiedades se deben pasar como cadenas. Así que aquí hay un ejemplo de cómo funciona:

//To set a property obj.set(''myProperty'',''myValue''); //To get a property var myVar = obj.get(''myProperty'');

Editar: Un enfoque mejorado, más orientado a objetos basado en lo que propuse es el siguiente:

function MyObject() { var emptyValue = null; var obj = {}; this.get = function(prop){ return (typeof obj[prop] == "undefined") ? emptyValue : obj[prop]; }; this.set = function(prop,value){ obj[prop] = value; }; } var newObj = new MyObject(); newObj.set(''myProperty'',''MyValue''); alert(newObj.get(''myProperty''));

Puedes verlo trabajando here .


Actualización de 2013 y 2015 (ver a continuación la respuesta original de 2011) :

Esto cambió a partir de la especificación ES2015 (también conocida como "ES6"): JavaScript ahora tiene proxies . Los proxies le permiten crear objetos que son verdaderos proxies para (fachadas) otros objetos. Aquí hay un ejemplo simple que convierte los valores de propiedad que son cadenas en mayúsculas en la recuperación:

var original = { "foo": "bar" }; var proxy = new Proxy(original, { get: function(target, name, receiver) { var rv = target[name]; if (typeof rv === "string") { rv = rv.toUpperCase(); } return rv; } }); console.log("original.foo = " + original.foo); // "bar" console.log("proxy.foo = " + proxy.foo); // "BAR"

"use strict"; (function() { if (typeof Proxy == "undefined") { console.log("This browser doesn''t support Proxy"); return; } var original = { "foo": "bar" }; var proxy = new Proxy(original, { get: function(target, name, receiver) { var rv = target[name]; if (typeof rv === "string") { rv = rv.toUpperCase(); } return rv; } }); console.log("original.foo = " + original.foo); // "bar" console.log("proxy.foo = " + proxy.foo); // "BAR" })();

Las operaciones que no anulas tienen su comportamiento predeterminado. En lo anterior, todo lo que anulamos es get , pero hay una lista completa de operaciones en las que puede enganchar.

En la lista de argumentos de la función get handler:

  • target es el objeto que se está proxys ( original , en nuestro caso).
  • name es (por supuesto) el nombre de la propiedad que se recupera.
  • receiver es el proxy en sí o algo que hereda de él. En nuestro caso, el receiver es === proxy , pero si el proxy se usó como prototipo, el receiver podría ser un objeto descendiente, por lo tanto, está en la firma de la función (pero al final, por lo que puede dejarlo si, como con nuestro ejemplo anterior, en realidad no lo usa).

Esto le permite crear un objeto con la función getter and setter catch-all que desea:

var obj = new Proxy({}, { get: function(target, name) { if (!(name in target)) { console.log("Getting non-existant property ''" + name + "''"); return undefined; } return target[name]; }, set: function(target, name, value) { if (!(name in target)) { console.log("Setting non-existant property ''" + name + "'', initial value: " + value); } target[name] = value; return true; } }); console.log("[before] obj.foo = " + obj.foo); obj.foo = "bar"; console.log("[after] obj.foo = " + obj.foo);

"use strict"; (function() { if (typeof Proxy == "undefined") { console.log("This browser doesn''t support Proxy"); return; } var obj = new Proxy({}, { get: function(target, name) { if (!(name in target)) { console.log("Getting non-existant property ''" + name + "''"); return undefined; } return target[name]; }, set: function(target, name, value) { if (!(name in target)) { console.log("Setting non-existant property ''" + name + "'', initial value: " + value); } target[name] = value; return true; } }); console.log("[before] obj.foo = " + obj.foo); obj.foo = "bar"; console.log("[after] obj.foo = " + obj.foo); })();

(Observe cómo he dejado el receiver fuera de las funciones, ya que no lo usamos. El receiver es un cuarto arg opcional en el set ).

El resultado de lo anterior es:

Getting non-existant property ''foo'' [before] obj.foo = undefined Setting non-existant property ''foo'', initial value: bar [after] obj.foo = bar

Observe cómo obtenemos el mensaje "inexistente" cuando tratamos de recuperar foo cuando aún no existe, y nuevamente cuando lo creamos, pero no subsecuentemente.

Respuesta de 2011 (ver arriba para las actualizaciones de 2013 y 2015) :

No, JavaScript no tiene una función de propiedad catch-all. La sintaxis de acceso que está utilizando está cubierta en la Sección 11.1.5 de la especificación, y no ofrece ningún comodín ni nada de eso.

Podría, por supuesto, implementar una función para hacerlo, pero supongo que probablemente no quiera usar f = obj.prop("foo"); en lugar de f = obj.foo; y obj.prop("foo", value); en lugar de obj.foo = value; (que sería necesario para que la función maneje propiedades desconocidas).

FWIW, la función getter (no me molesté con la lógica setter) se vería así:

MyObject.prototype.prop = function(propName) { if (propName in this) { // This object or its prototype already has this property, // return the existing value. return this[propName]; } // ...Catch-all, deal with undefined property here... };

Pero nuevamente, no puedo imaginar que realmente quieras hacer eso, debido a cómo cambia la forma en que usas el objeto.


var x={} var propName = ''value'' var get = Function("return this[''" + propName + "'']") var set = Function("newValue", "this[''" + propName + "''] = newValue") var handler = { ''get'': get, ''set'': set, enumerable: true, configurable: true } Object.defineProperty(x, propName, handler)

esto funciona para mí