是否可以在JavaScript中实现动态getter / setter? [英] Is it possible to implement dynamic getters/setters in JavaScript?
问题描述
我知道如何为名字已经知道的属性创建getter和setter,通过这样做:
I am aware of how to create getters and setters for properties whose names one already knows, by doing something like this:
// A trivial example:
function MyObject(val){
this.count = 0;
this.value = val;
}
MyObject.prototype = {
get value(){
return this.count < 2 ? "Go away" : this._value;
},
set value(val){
this._value = val + (++this.count);
}
};
var a = new MyObject('foo');
alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"
现在,我的问题是,是否有可能定义一些全能的吸气剂和像这样的二传手?即,为未定义的任何属性名称创建getter和setter。
Now, my question is, is it possible to define sort of catch-all getters and setters like these? I.e., create getters and setters for any property name which isn't already defined.
使用<$ c在PHP中可以实现这个概念$ c> __ get()和 __ set()
魔术方法(参见 PHP文档以获取有关这些的信息),所以我真的要问是否有相当于这些的JavaScript?
The concept is possible in PHP using the __get()
and __set()
magic methods (see the PHP documentation for information on these), so I'm really asking is there a JavaScript equivalent to these?
毋庸置疑,我非常喜欢跨浏览器兼容的解决方案。
Needless to say, I'd ideally like a solution that is cross-browser compatible.
推荐答案
2013年和2015年更新 (请参阅以下2011年的原始答案):
此更改为ES2015(又名ES6)规范:JavaScript现在有 代理 。代理允许您创建对象(外观)其他对象的真实代理。这是一个简单的示例,它将任何字符串的属性值转换为检索的所有上限:
This changed as of the ES2015 (aka "ES6") specification: JavaScript now has proxies. Proxies let you create objects that are true proxies for (facades on) other objects. Here's a simple example that turns any property values that are strings to all caps on retrieval:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let original = {
"foo": "bar"
};
let proxy = new Proxy(original, {
get(target, name, receiver) {
let rv = Reflect.get(target, name, receiver);
if (typeof rv === "string") {
rv = rv.toUpperCase();
}
return rv;
}
});
console.log(`original.foo = ${original.foo}`); // "original.foo = bar"
console.log(`proxy.foo = ${proxy.foo}`); // "proxy.foo = BAR"
你要做的操作t override具有默认行为。在上面,我们覆盖的只是 get
,但是你可以勾选一整套操作。
Operations you don't override have their default behavior. In the above, all we override is get
, but there's a whole list of operations you can hook into.
在 get
处理函数的参数列表中:
In the get
handler function's arguments list:
-
target
是被代理的对象(在我们的例子中是原始
)。 -
name
(当然)是要检索的属性的名称,通常是字符串,但也可以是符号。 -
如果属性是访问者而不是数据属性,接收器
是getter函数中应该用作this
的对象。在正常情况下,这是代理或继承自它的东西,但它可以是任何东西,因为陷阱可能由Reflect.get
触发。
target
is the object being proxied (original
, in our case).name
is (of course) the name of the property being retrieved, which is usually a string but could also be a Symbol.receiver
is the object that should be used asthis
in the getter function if the property is an accessor rather than a data property. In the normal case this is the proxy or something that inherits from it, but it can be anything since the trap may be triggered byReflect.get
.
这可以让你创建一个具有你想要的全能getter和setter功能的对象:
This lets you create an object with the catch-all getter and setter feature you want:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let obj = new Proxy({}, {
get(target, name, receiver) {
if (!Reflect.has(target, name)) {
console.log("Getting non-existent property '" + name + "'");
return undefined;
}
return Reflect.get(target, name, receiver);
},
set(target, name, value, receiver) {
if (!Reflect.has(target, name)) {
console.log(`Setting non-existent 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}`);
上面的输出是:
Getting non-existent property 'foo'
[before] obj.foo = undefined
Setting non-existent property 'foo', initial value: bar
[after] obj.foo = bar
注意我们在尝试检索时如何获取不存在的消息 foo
当它还不存在时,我们再创建它时,但不是在那之后。
Note how we get the "non-existent" message when we try to retrieve foo
when it doesn't yet exist, and again when we create it, but not after that.
2011年回答 (见上文2013和2015年更新):
不,JavaScript没有全面的属性功能。您正在使用的访问器语法包含在规范的第11.1.5节中,并且没有提供任何通配符或类似的东西。
No, JavaScript doesn't have a catch-all property feature. The accessor syntax you're using is covered in Section 11.1.5 of the spec, and doesn't offer any wildcard or something like that.
当然,你可以实现一个功能,但我猜你可能不想要使用 f = obj.prop(foo);
而不是 f = obj.foo;
和 obj.prop(foo,value);
而不是 obj.foo = value;
(这对于用于处理未知属性的函数。)
You could, of course, implement a function to do it, but I'm guessing you probably don't want to use f = obj.prop("foo");
rather than f = obj.foo;
and obj.prop("foo", value);
rather than obj.foo = value;
(which would be necessary for the function to handle unknown properties).
FWIW,getter函数(我没有打扰setter逻辑)看起来像这样:
FWIW, the getter function (I didn't bother with setter logic) would look something like this:
MyObject.prototype.prop = function(propName) {
if (propName in this) {
// This object or its prototype already has this property,
// return the existing value.
return this[propName];
}
// ...Catch-all, deal with undefined property here...
};
但是再一次,我无法想象你真的想要那样做,因为它是怎么回事改变你使用对象的方式。
But again, I can't imagine you'd really want to do that, because of how it changes how you use the object.
这篇关于是否可以在JavaScript中实现动态getter / setter?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!