我知道如何通过执行以下操作来为名称已经知道的属性创建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。

这个概念在PHP中可以使用__get()__set()魔术方法(有关这些信息,请参见the PHP documentation),所以我真的在问是否存在与这些方法等效的JavaScript?

不用说,理想情况下,我想要一个跨浏览器兼容的解决方案。

最佳答案

2013年和2015年更新(请参见下面的2011年原始答案):

自ES2015(又称“ ES6”)规范以来,此更改已更改:JavaScript现在具有proxies。代理使您可以创建真正的对象(作为其他对象的外观)。这是一个简单的示例,该示例将所有字符串形式的属性值转换为检索时的所有大写字母:



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





您未覆盖的操作具有其默认行为。在上面,我们覆盖的全部是get,但是有一个可以挂接到的操作的完整列表。

get处理函数的参数列表中:


target是要代理的对象(在本例中为original)。
(当然)name是要检索的属性的名称,通常是一个字符串,但也可以是一个符号。
如果属性是访问器而不是数据属性,则receiver是应该在getter函数中用作this的对象。在正常情况下,这是代理服务器或从代理服务器继承的东西,但是可以是任何东西,因为陷阱可能由Reflect.get触发。


这使您可以使用所需的全部获取和设置器功能创建对象:



"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

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.


Answer from 2011 (see above for 2013 and 2015 updates):

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("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, 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...
};


但是再次,我无法想象您真的想这样做,因为它如何改变您使用对象的方式。

07-28 02:15
查看更多