是否可以在 JavaScript 中实现动态 getter/setter?
我知道如何为已经知道名称的属性创建 getter 和 setter,方法如下:
// 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?即,为任何尚未定义的属性名称创建 getter 和 setter。
这个概念在 PHP 中可以使用 __get()
和 __set()
魔术方法实现(参见 PHP 文档 有关这些的信息),所以我真的想问是否有与这些等效的 JavaScript?
不用说,我理想地希望有一个跨浏览器兼容的解决方案。
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"
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.
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
从 ES2015(又名“ES6”)规范开始,这种情况发生了变化:JavaScript 现在具有 代理。代理允许您创建作为其他对象(外观)的真正代理的对象。下面是一个简单的示例,它在检索时将任何字符串属性值全部大写,并针对不存在的属性返回
"missing"
而不是undefined
:您不覆盖的操作有其默认行为。在上面,我们覆盖的只是
get
,但是您可以挂接到一个完整的操作列表。在
get
处理函数的参数列表中:target
是被代理的对象(在我们的例子中是original
)。name
(当然)是正在检索的属性的名称,通常是字符串,但也可以是符号。receiver
是应该在 getter 函数中用作this
的对象。在正常情况下,这是代理或从它继承的东西,但它可以是任何东西,因为陷阱可能由Reflect.get
触发。这使您可以创建一个具有所需的所有 getter 和 setter 功能的对象:
上面的输出是:
请注意,当我们尝试检索尚不存在的
example
时,以及在创建它时再次收到“不存在”消息,但之后就不会了。2011 年的回答 (已被上述内容废弃,仍然与仅限于 ES5 功能的环境相关,例如 Internet Explorer):
不,JavaScript 没有包罗万象的属性功能。您使用的访问器语法包含在规范的 第 11.1.5 节 中,并且没有不提供任何通配符或类似的东西。
当然,您可以实现一个函数来执行此操作,但我猜您可能不想使用
f = obj.prop("example");
而不是f = obj.example;
和obj.prop("example", value);
而不是obj.example = value;
(这对于处理未知属性的函数)。FWIW,getter 函数(我不关心 setter 逻辑)看起来像这样:
但同样,我无法想象你真的想这样做,因为它改变了你使用对象的方式。
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, and returns
"missing"
instead ofundefined
for a property that doesn't exist: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.In the
get
handler function's arguments list: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
.This lets you create an object with the catch-all getter and setter feature you want:
The output of the above is:
Note how we get the "non-existent" message when we try to retrieve
example
when it doesn't yet exist, and again when we create it, but not after that.Answer from 2011 (obsoleted by the above, still relevant to environments limited to ES5 features like Internet Explorer):
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.
You could, of course, implement a function to do it, but I'm guessing you probably don't want to use
f = obj.prop("example");
rather thanf = obj.example;
andobj.prop("example", value);
rather thanobj.example = value;
(which would be necessary for the function to handle unknown properties).FWIW, the getter function (I didn't bother with setter logic) would look something like this:
But again, I can't imagine you'd really want to do that, because of how it changes how you use the object.
前言:
TJ Crowder 的回答提到了一个
Proxy
,它将是正如OP所要求的那样,对于不存在的属性需要一个包罗万象的getter/setter。根据动态 getter/setter 实际需要的行为,代理实际上可能不是必需的;或者,您可能希望将代理
与我将在下面向您展示的内容结合使用。(PS,我最近在 Linux 上的 Firefox 中彻底尝试了
Proxy
,发现它非常强大,但也有些令人困惑/难以使用和正确使用。更重要的是,我有还发现它相当慢(至少相对于当今 JavaScript 的优化程度而言)——我说的是慢十倍的范围。)为了具体实现动态创建的 getter 和 setter,你需要可以使用
Object.defineProperty()
< /a> 或Object.defineProperties()
。这也是相当快的。要点是您可以在对象上定义 getter 和/或 setter,如下所示:
这里需要注意的几件事:
value
属性(不能 如上所示)与get
和/或set
同时进行;来自文档:<块引用>
对象中存在的属性描述符有两种主要类型:数据描述符和访问器描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能是不可写的。访问器描述符是由一对 getter-setter 函数描述的属性。描述符必须是这两种风格之一;不可能两者兼而有之。
Object.defineProperty()
调用/属性描述符的外部创建了一个val
属性。这是标准行为。writable
设置为true
如果您使用get
或set
,则在属性描述符中。configurable
和enumerable
,具体取决于您的需求;来自文档:<块引用>
可配置
true
当且仅当该属性描述符的类型可以更改并且该属性可以从相应的对象中删除时。默认为 false。
<小时>
可枚举
true
当且仅当该属性在相应对象的属性枚举期间出现。默认为 false。
就此而言,这些也可能令人感兴趣:
Object.getOwnPropertyNames(obj)
:获取对象的所有属性,甚至是不可枚举的属性(据我所知这是唯一这样做的方法!)。Object.getOwnPropertyDescriptor(obj, prop)
:获取对象的属性描述符,该对象是上面传递给Object.defineProperty()
的对象。obj.propertyIsEnumerable(prop);< /code>
:对于特定对象实例上的单个属性,在该对象实例上调用此函数来确定该特定属性是否可枚举。
Preface:
T.J. Crowder's answer mentions a
Proxy
, which will be needed for a catch-all getter/setter for properties which don't exist, as the OP was asking for. Depending on what behavior is actually wanted with dynamic getters/setters, aProxy
may not actually be necessary though; or, potentially, you may want to use a combination of aProxy
with what I'll show you below.(P.S. I have experimented with
Proxy
thoroughly in Firefox on Linux recently and have found it to be very capable, but also somewhat confusing/difficult to work with and get right. More importantly, I have also found it to be quite slow (at least in relation to how optimized JavaScript tends to be nowadays) - I'm talking in the realm of deca-multiples slower.)To implement dynamically created getters and setters specifically, you can use
Object.defineProperty()
orObject.defineProperties()
. This is also quite fast.The gist is that you can define a getter and/or setter on an object like so:
Several things to note here:
value
property in the property descriptor (not shown above) simultaneously withget
and/orset
; from the docs:val
property outside of theObject.defineProperty()
call/property descriptor. This is standard behavior.writable
totrue
in the property descriptor if you useget
orset
.configurable
andenumerable
, however, depending on what you're after; from the docs:On this note, these may also be of interest:
Object.getOwnPropertyNames(obj)
: gets all properties of an object, even non-enumerable ones (AFAIK this is the only way to do so!).Object.getOwnPropertyDescriptor(obj, prop)
: gets the property descriptor of an object, the object that was passed toObject.defineProperty()
above.obj.propertyIsEnumerable(prop);
: for an individual property on a specific object instance, call this function on the object instance to determine whether the specific property is enumerable or not.以下可能是解决此问题的原始方法:
为了使用它,属性应作为字符串传递。
以下是其工作原理的示例:
编辑:
基于我提出的改进的、更加面向对象的方法如下:
您可以在此处。
The following could be an original approach to this problem:
In order to use it the properties should be passed as strings.
So here is an example of how it works:
Edit:
An improved, more object-oriented approach based on what I proposed is the following:
You can see it working here.
我一直在寻找一些东西,然后我自己找到了答案。
I was looking for something and I figured out on my own.
这对我有用
this works for me