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
Javascript - catch access to property of object
提问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? bar
may be undefined on Foo
. I could suffice with capturing any attempted access to undefined properties.
有没有办法(也许是原型)来捕捉这个?bar
上可能未定义Foo
。我可以捕获对未定义属性的任何尝试访问。
For instance, if bar
is undefined on Foo
, and Foo.bar
is 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 Proxy
object in ECMAScript6. Meanwhile, depending on your needs and overall design, you can still achieve this by implementing something similar.
就像已经回答的那样,只能使用Proxy
ECMAScript6 中的对象。同时,根据您的需求和整体设计,您仍然可以通过实现类似的东西来实现这一点。
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
, 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.
随着新的defineProperties
,defineGetter
并且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