Javascript - 捕获对对象属性的访问

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20147081/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 17:52:15  来源:igfitidea点击:

Javascript - catch access to property of object

javascriptobject-properties

提问by Randy Hall

Is it possible to capture when a (any) property of an object is accessed, or attempting to be accessed?

是否可以在访问或尝试访问对象的(任何)属性时进行捕获?

Example:

例子:

I have created custom object Foo

我创建了自定义对象 Foo

var Foo = (function(){
    var self = {};
    //... set a few properties
    return self;
})();

Then there is some action against Foo- someone tries to access property bar

然后有一些行动反对Foo- 有人试图访问财产bar

Foo.bar

Is there way (prototype, perhaps) to capture this? barmay be undefined on Foo. I could suffice with capturing any attempted access to undefined properties.

有没有办法(也许是原型)来捕捉这个?bar上可能未定义Foo。我可以捕获对未定义属性的任何尝试访问。

For instance, if baris undefined on Foo, and Foo.baris attempted, something like:

例如,如果bar在 上未定义Foo,并Foo.bar尝试使用,例如:

Foo.prototype.undefined = function(){
    var name = this.name; //name of property they attempted to access (bar)
    Foo[name] = function(){
        //...do something
    };
    return Foo[name];
}

But functional, unlike my example.

但功能,不像我的例子。

Concept

概念

Foo.* = function(){
}

Background

背景

If I have a custom function, I can listen for every time this function is called (see below). Just wondering if it's possible with property access.

如果我有一个自定义函数,我可以在每次调用这个函数时监听(见下文)。只是想知道是否可以访问属性。

Foo = function(){};
Foo.prototype.call = function(thisArg){
    console.log(this, thisArg);
    return this;
}

回答by T.J. Crowder

Yes, this is possible in ES2015+, using the Proxy. It's not possible in ES5 and earlier, not even with polyfills.

是的,这在 ES2015+ 中是可能的,使用Proxy。这在 ES5 及更早版本中是不可能的,甚至在 polyfills 中也不可能。

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

我花了一段时间,但我终于找到了我以前对这个问题的答案。有关代理等的所有详细信息,请参阅该答案。

Here's the proxy example from that answer:

这是该答案中的代理示例:

const obj = new Proxy({}, {
    get: function(target, name, receiver) {
        if (!(name in target)) {
            console.log("Getting non-existant property '" + name + "'");
            return undefined;
        }
        return Reflect.get(target, name, receiver);
    },
    set: function(target, name, value, receiver) {
        if (!(name in target)) {
            console.log("Setting non-existant property '" + name + "', initial value: " + value);
        }
        return Reflect.set(target, name, value, receiver);
    }
});

console.log("[before] obj.foo = " + obj.foo);
obj.foo = "bar";
console.log("[after] obj.foo = " + obj.foo);
obj.foo = "baz";
console.log("[after] obj.foo = " + obj.foo);

Live Copy:

实时复制:

"use strict";

const obj = new Proxy({}, {
    get: function(target, name, receiver) {
        if (!(name in target)) {
            console.log("Getting non-existant property '" + name + "'");
            return undefined;
        }
        return Reflect.get(target, name, receiver);
    },
    set: function(target, name, value, receiver) {
        if (!(name in target)) {
            console.log("Setting non-existant property '" + name + "', initial value: " + value);
        }
        return Reflect.set(target, name, value, receiver);
    }
});

console.log("[before] obj.foo = " + obj.foo);
obj.foo = "bar";
console.log("[after] obj.foo = " + obj.foo);
obj.foo = "baz";
console.log("[after] obj.foo = " + obj.foo);

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
[after] obj.foo = baz

回答by Katana314

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 veryuseful for testing code that does something you don't want it to. But, I remove it for production code.

我将在假设您正在尝试调试某些内容的情况下编写此内容。正如 Crowder 所说,这仅适用于较新的浏览器;因此它对于测试执行您不希望它执行的操作的代码非常有用。但是,我将其删除以用于生产代码。

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.

看起来 megavac 打败了我。您还可以在此处找到有关这些功能的一些 Mozilla 文档。

回答by plalx

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

就像已经回答的那样,只能使用ProxyECMAScript6 中的对象。同时,根据您的需求和整体设计,您仍然可以通过实现类似的东西来实现这一点。

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;

回答by Tibos

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

回答by megawac

With the new defineProperties, defineGetterand defineSetterbeing 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.

随着新的definePropertiesdefineGetter并且defineSetter被添加到JavaScript中,你可以做有点类似。__properties__然而,仍然没有真正的方法来隐藏对象的 。我建议你看看这篇文章

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