首页 文章

是否可以在JavaScript中实现动态getter / setter?

提问于
浏览
108

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

不用说,我理想地喜欢跨浏览器兼容的解决方案 .

4 回答

  • 175

    2013 and 2015 Update (见以下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 触发 .

    这使您可以使用所需的catch-all getter和setter功能创建一个对象:

    "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 时,如果它们尚不存在,我们将如何获取"non-existent"消息,并且当我们创建它时再次检索"non-existent"消息,但之后不会 .


    Answer from 2011 (参见上面的2013年和2015年更新):

    不,JavaScript没有't have a catch-all property feature. The accessor syntax you'重新使用在规范的Section 11.1.5中,并且不提供任何通配符或类似的东西 .

    当然,您可以实现一个函数来执行此操作,但是我想要使用 f = obj.prop("foo"); 而不是 f = obj.foo;obj.prop("foo", value); 而不是 obj.foo = value; (这是函数处理未知属性所必需的) .

    FWIW,getter函数(我没有使用setter逻辑)看起来像这样:

    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...
    };
    

    但同样,我无法想象你真的想要这样做,因为它改变了你如何使用这个对象 .

  • 6

    在现代Javascript(FF4,IE9,Chrome 5,Safari 5.1,Opera 11.60)中,有 Object.defineProperty . This example on the MDN很好地解释了 defineProperty 如何工作,并使动态getter和setter成为可能 .

    从技术上讲,这不适用于您正在寻找的任何动态查询,但是如果您的有效getter和setter是由例如对JSON-RPC服务器的AJAX调用定义的,那么您可以在以下方式:

    arrayOfNewProperties.forEach(function(property) {
        Object.defineProperty(myObject, property.name, {
            set: property.setter, get: property.getter
        });
    });
    
  • -4

    以下可能是解决此问题的原始方法:

    var obj = {
      emptyValue: null,
      get: function(prop){
        if(typeof this[prop] == "undefined")
            return this.emptyValue;
        else
            return this[prop];
      },
      set: function(prop,value){
        this[prop] = value;
      }
    }
    

    为了使用它,属性应该作为字符串传递 . 所以这是一个如何工作的例子:

    //To set a property
    obj.set('myProperty','myValue');
    
    //To get a property
    var myVar = obj.get('myProperty');
    

    Edit: 基于我提出的改进的,更加面向对象的方法如下:

    function MyObject() {
        var emptyValue = null;
        var obj = {};
        this.get = function(prop){
            return (typeof obj[prop] == "undefined") ? emptyValue : obj[prop];
        };
        this.set = function(prop,value){
            obj[prop] = value;
        };
    }
    
    var newObj = new MyObject();
    newObj.set('myProperty','MyValue');
    alert(newObj.get('myProperty'));
    

    你可以看到它工作here .

  • 44
    var x={}
    var propName = 'value' 
    var get = Function("return this['" + propName + "']")
    var set = Function("newValue", "this['" + propName + "'] = newValue")
    var handler = { 'get': get, 'set': set, enumerable: true, configurable: true }
    Object.defineProperty(x, propName, handler)
    

    这对我有用

相关问题