body - Javascript-captura el acceso a la propiedad del objeto
title html (5)
¿Es posible capturar cuando se accede a una (cualquiera) propiedad de un objeto, o se intenta acceder?
Ejemplo:
He creado el objeto personalizado Foo
var Foo = (function(){
var self = {};
//... set a few properties
return self;
})();
Luego hay algo de acción contra Foo
: alguien intenta acceder a la bar
propiedades.
Foo.bar
¿Hay manera (prototipo, tal vez) de capturar esto? bar
puede estar indefinida en Foo
. Podría bastar con capturar cualquier intento de acceso a propiedades no definidas.
Por ejemplo, si la bar
no está definida en Foo
, y se intenta Foo.bar
, algo como:
Foo.prototype.undefined = function(){
var name = this.name; //name of property they attempted to access (bar)
Foo[name] = function(){
//...do something
};
return Foo[name];
}
Pero funcional, a diferencia de mi ejemplo.
Concepto
Foo.* = function(){
}
Fondo
Si tengo una función personalizada, puedo escuchar cada vez que se llama a esta función (ver más abajo). Solo me pregunto si es posible con acceso a la propiedad.
Foo = function(){};
Foo.prototype.call = function(thisArg){
console.log(this, thisArg);
return this;
}
Como mencionaron las otras respuestas, en este momento no hay manera de interceptar propiedades indefinidas.
¿Sería esto aceptable sin embargo?
var myObj = (function() {
var props = {
foo : ''foo''
}
return {
getProp : function(propName) { return (propName in props) ? props[propName] : ''Nuh-uh!'' }
}
}());
console.log(myObj.getProp(''foo'')); // foo
console.log(myObj.getProp(''bar'')); // Nuh-uh
Como ya se ha respondido, solo será posible usando el objeto Proxy
en ECMAScript6. Mientras tanto, dependiendo de sus necesidades y diseño general, aún puede lograr esto implementando algo similar.
P.ej
function WrappingProxy(object, noSuchMember) {
if (!this instanceof WrappingProxy) return new WrappingProxy(object);
this._object = object;
if (noSuchMember) this.noSuchMember = noSuchMember;
}
WrappingProxy.prototype = {
constructor: WrappingProxy,
get: function (propertyName) {
var obj = this._object;
if (propertyName in obj) return obj[propertyName];
if (this.noSuchMember) this.noSuchMember(propertyName, ''property'');
},
set: function (propertyName, value) {
return this._object[propertyName] = value;
},
invoke: function (functionName) {
var obj = this._object,
args = Array.prototype.slice.call(arguments, 1);
if (functionName in obj) return obj[functionName].apply(obj, args);
if (this.noSuchMember) {
this.noSuchMember.apply(obj, [functionName, ''function''].concat(args));
}
},
object: function() { return this._object },
noSuchMember: null
};
var obj = new WrappingProxy({
testProp: ''test'',
testFunc: function (v) {
return v;
}
},
//noSuchMember handler
function (name, type) {
console.log(name, type, arguments[2]);
}
);
obj.get(''testProp''); //test
obj.get(''nonExistingProperty''); //undefined, call noSuchMember
obj.invoke(''testFunc'', ''test''); //test
obj.invoke(''nonExistingFunction'', ''test''); //undefined, call noSuchMember
//accesing properties directly on the wrapped object is not monitored
obj.object().nonExistingProperty;
Con las nuevas defineProperties
, defineGetter
y defineSetter
agregadas a javascript, puede hacer algo similar. Sin embargo, todavía no existe una forma verdadera de ocultar las __properties__
de un objeto. Te sugiero que veas este article .
var obj = {
__properties__: {
a: 4
}
}
Object.defineProperties(obj, {
"b": { get: function () { return this.__properties__.a + 1; } },
"c": { get: function (x) { this.__properties__.a = x / 2; } }
});
obj.b // 2
obj.c // .5
Este es el tipo clásico de modelo que debería funcionar en cualquier entorno.
//lame example of a model
var Model = function(a) {
this.__properties__ = {a: a};
}
Model.prototype.get = function(item) {
//do processing
return this.__properties__[item];
}
Model.prototype.set = function(item, val) {
//do processing
this.__properties__[item] = val;
return this;
}
var model = new Model(5);
model.get("a") // => 5
Escribiré esto bajo el supuesto de que estás tratando de depurar algo. Como dijo Crowder, esto solo está disponible en los navegadores más nuevos; así que es muy útil para probar código que hace algo que no quieres que haga. Pero, lo quito por código de producción.
Object.defineProperty(Foo, ''bar'', {
set: function() {
debugger; // Here is where I''ll take a look in the developer console, figure out what''s
// gone wrong, and then remove this whole block.
}
});
Parece que Megawac me venció. También puede encontrar documentación de Mozilla sobre las características here .
Esto será posible en ECMAScript6, y es posible ahora mismo en Firefox, usando el nuevo proxy . Hasta entonces, no, me temo que no hay forma de engancharse a la cadena.
Me tomó un tiempo, pero finalmente encontré mi respuesta anterior a esta pregunta. Ver esa respuesta para todos los detalles sobre proxies y tal.
Aquí está el ejemplo de proxy de esa respuesta:
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;
}
});
console.log("[before] obj.foo = " + obj.foo);
obj.foo = "bar";
console.log("[after] obj.foo = " + obj.foo);
Copia en vivo (actualmente solo funciona en Firefox) | Source
Cuando se ejecuta, eso produce:
Getting non-existant property ''foo'' [before] obj.foo = undefined Setting non-existant property ''foo'', initial value: bar [after] obj.foo = bar