Javascript - catch access to property of object

£可爱£侵袭症+ 提交于 2019-11-26 22:14:48
T.J. Crowder

This will be possible in ECMAScript6, and is possible right now on Firefox, using the new proxy stuff. Until then, no, I'm afraid there's no way to hook into the chain.

It took me a while, but I finally found my previous answer to this question. See that answer for all the details on proxies and such.

Here's the proxy example from that answer:

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);

Live Copy (currently only works on Firefox) | Source

When run, that outputs:

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

I'll write this under the assumption you're trying to debug something. As Crowder said, this is only available on newer browsers; so it's very useful for testing code that does something you don't want it to. But, I remove it for production code.

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.
  }
});

Looks like megawac beat me to it. You can also find some Mozilla documentation on the features here.

Like answered already, it will only be possible using the Proxy object in ECMAScript6. Meanwhile, depending on your needs and overall design, you can still achieve this by implementing something similar.

E.g.

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;

With the new defineProperties, defineGetter and defineSetter being added to javascript, you can do something somewhat similar. There is still no true way to hide the __properties__ of an object however. I suggest you see this 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

This is the classic sort of model that should work in any environment

//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

As the other answers mentioned, at the moment there is no way to intercept undefined properties.

Would this be acceptable though?

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!