Búsqueda de sitios web

javascript - getters y setters dinámicos - una posibilidad


Estoy tratando de resolver un problema que se me ocurrió últimamente. Digamos que quisiéramos y supiéramos cómo hacer un punto en tener getters y setters dinámicos en JavaScript, más como los que hay en PHP (__get, __set). Pero como JavaScript no tiene una propiedad de captura general, lo único que podríamos hacer es proporcionar una lista de claves posibles e iterar para agregar getters y setters solo en esas, y esperar que nunca aparezca ninguna otra.

Pero el problema está lejos de estar solucionado. Así que el siguiente enfoque que se me ocurrió fue usar un truco desagradable con try y catch, de modo que cada vez que un nombre estuviera indefinido en un objeto, se usara el catch como un getter (al menos) y luego reanudar el código, algo difícil y tal vez inútil de hacer. Pero de aquí surgió mi segundo problema, en un uso como este:

console.log(g.someundefinedproperty); 

el resultado habría sido una llamada a console.log mostrando undefined sin que se lanzara ninguna excepción. Y entonces se me ocurrió: ¿y si uso el getter y setter original de window.undefined, después de todo debe ser llamado cada vez que me equivoque y escriba mal una palabra o algo así.

Así que lo intenté

Object.defineProperty(window, 'undefined', {
    get : function ()
    {
         // functional code, including getting the caller and figuring out
         // where we are, and what we have to do... easy :D

         console.log('works');
    },
    set : function ()
    {
         // some couple more fine hacks here
         console.log('this too');
    }
});

Pero desafortunadamente la propiedad undefined de window es configurable : false. Otros trucos intentados fueron clonar el objeto window excepto la propiedad undefined y la propiedad interna window. Y en el nuevo objeto definir el nuevo undefined (por favor tomen nota de la ironía) y luego window=mybetterwindow;

Como esto no surgió ningún problema, mis esperanzas eran altas, pero una vez más el sistema me falló ya que window no puede ser sobrescrito, por diseño. Hice una suposición de que tiene su propio getter y se reinstancia a sí mismo basado en el prototipo encontrado en window.prototype o incluso mejor Window.prototype (nota la letra mayúscula).

Como mi último paso en este experimento, había redefinido undefined en este prototipo y presioné ejecutar. De nada sirvió, nada cambió... Intenté crear un new Window(), pero Window no es un constructor, ¡fallo!

Como me he quedado sin ideas, me encuentro aquí escribiendo esto suplicando ayuda. Si tienes alguna idea de cómo resolver el problema de los getters y setters dinámicos, (el problema existencial de la vida, el universo y todo lo demás), de una manera que no modifique de ninguna forma la... manera en que uso los objetos (y como bonificación no necesita romper un agujero en el tejido del tiempo y el espacio) ni la sintaxis, te ruego que hables o permanezcas en silencio para siempre :).

Respuesta #1

Pero desafortunadamente la propiedad indefinida de window es configurable: false

Esto es cierto solo desde EcmaScript 5.1. Antes, era sobrescribible.

¿Qué pasaría si usara el getter y setter originales de window.undefined, después de todo, debe llamarse cada vez que cometo un error y escribo mal una palabra o algo así.

No, no habría funcionado. Hay una diferencia entre el valor undefined y la variable global "undefined". La variable no se evalúa cada vez que se encuentra un valor undefined (por ejemplo, en typeof (void 0)), solo cuando la usas explícitamente (como en g.someprop === undefined).

¿Alguna idea de cómo resolver el problema de los getters y setters dinámicos?

Solo hay una solución: Proxies. Desafortunadamente, es solo un borrador de armonía y actualmente solo es compatible con Javascript 1.8.5 de Firefox.

Véase también ¿Existe un equivalente de la característica __noSuchMethod__ para propiedades, o una manera de implementarla en JS?, ¿Detectar cuándo se agrega una nueva propiedad a un objeto de Javascript? o ¿Cómo detectar cuándo se agrega una propiedad a un objeto de JavaScript?, que implementa una solución de sondeo (comprobando cambios mediante setInterval).

Para una solución limpia, actualmente tienes que usar una función getter explícita, a la que le pasas el nombre de la propiedad (g.get("someundefinedproperty")).

Respuesta #2

Lo que estás tratando de lograr (un getter global para cualquier propiedad indefinida) está fuera del alcance del JavaScript multiplataforma.

La funcionalidad más cercana sería usar __defineGetter__ para agregar una propiedad particular a todos los objetos:

var o = Object.prototype;
o.__defineGetter__("testo", function() { return "yo in testo"; });
alert({}.testo);
alert((new Date()).testo);

http://jsfiddle.net/NLCvs/2/

Esto puede ser útil si puedes predecir el nombre de propiedades posiblemente indefinidas con antelación. Aun así, es bastante improvisado y de mala forma añadir un accesor a todos los objetos para una propiedad que podría estar indefinida.

Una mejor opción es refactorizar el código cuando se pueda acceder a una propiedad indefinida:

function getter(o, n) {
 return typeof o[n] == 'undefined' ? 'some default value' : o[n];
}
var obj = { hello: 'world' };
alert(getter(obj, 'hello'));
alert(getter(obj, 'an_undefined_property'));

Una opción aún mejor es usar un mixin para establecer cualquier propiedad predeterminada que necesites acceder, por ejemplo, mediante jQuery.extend

var defaults = {
 required: 'this property is required'
};
var my_object = {
 something_else: 'this is something else'
};
var o = jQuery.extend({}, defaults, my_object);

// o.required === 'this property is required'
// o.something_else === 'this is something else'