【问题标题】:Generate generic getters and setters for entity properties using decorators使用装饰器为实体属性生成通用 getter 和 setter
【发布时间】:2015-07-04 19:47:54
【问题描述】:

我正在将我的模型实体转换为 Typescript(使用 ajax 调用填充它们)。我希望每个实体中的每个属性都有一个基本的 getter 和 setter:

entity.setProductID("2r3e4r4t5y6t5y6") and entity.getProductID()

目前,我正在为类中的每个属性定义这些方法

   /**
    *@name "productID"
    *@ormtype "string"
    *@length "32"
    *@fieldtype "id"
    *@generator "uuid"
    *@unsavedvalue ""
    */
    private productID: string;

    getProductID():string { return this.productID; }
    setProductID(productID:string) { this.productID = productID; }

但我每个实体大约有 30 - 80 个属性和 150 个实体,所以代码对于 getter 和 setter 定义有点冗长。

为了解决这个问题,我希望使用一个装饰器 @generateGetterAndSetter 来装饰每个属性,它会为我动态生成 getter 和 setter。看起来像:

@generateGetterAndSetter
private productID: string;

为了实现这一点,我定义了装饰器并将其添加到我的模块中:

/** decorate the property to attach a getter and setter */
function generateGetterAndSetter(target: any, key: string) {

  // property value
  var _val = this[key];

  // property getter
 var getMethodName = 'get' + key.charAt(0).toUpperCase() + key.slice(1);
 var setMethodName = 'set' + key.charAt(0).toUpperCase() + key.slice(1);

  /** Build the getter and setter functions dynamically */
  var getter = new Function("return function " + getMethodName + "(){  return _val;}")();
  var setter = new Function("return function " + setMethodName + "(newVal){  _val = newVal;}")();

  target[getMethodName] = getter;
  target[setMethodName] = setter;

  Object.defineProperty(target, key, {
      get: getter,
      set: setter,
      enumerable: true,
      configurable: true
  });
}//<--end generateGetterAndSetterDefinition

从上面的代码中,您可以看到我已经尝试过 Object.defineProperty 以及手动将函数添加到目标。但在测试时:

var product = new Product();
product.setProductID("123456789");  
product.setProductName("Generic Shoes");
product.setUrlTitle("http://some/brand/get");   

console.log("Product: ", product.getProductName(), product.getProductID(), product.getUrlTitle());

它会产生不正确的结果:

Product: http://some/brand/get http://some/brand/get http://some/brand/get

而不是预期

Product: Generic Shoes 123456789 http://some/brand/get

这肯定是因为我覆盖了每个作业的定义。有没有更好的方法来解决这个问题,而不必为每个属性包含一个 get/set 方法,同时仍然保持 getPropertyName() 和 setPropertyName(value) 的正确函数命名约定?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    我不知道ts。但是自 ES2015(又名“ES6”)规范以来,JavaScript 现在有了 proxies。代理允许您创建真正代理其他对象(立面)的对象。

    var obj = new Proxy({}, {
    get: function(target, name) {
        if (!(name in target)) {
            console.log("Getting non-existant property '" + name + "'");
            return undefined;
        }
        return target[name];
    },
    set: function(target, name, value) {
        if (!(name in target)) {
            console.log("Setting non-existant property '" + name + "', initial value: " + value);
        }
        target[name] = value;
        return true;
    }
    });
    
    console.log("[before] obj.foo = " + obj.foo);
    obj.foo = "bar";
    console.log("[after] obj.foo = " + obj.foo);
    

    【讨论】:

    • 谢谢,我没有听说过代理。是否可以绑定this 上下文或将任何额外的参数传递给处理程序?我想从处理程序内部访问外部类的方法。
    • 我在这个 FWIW 上添加了一个问题:stackoverflow.com/questions/64996534/…
    【解决方案2】:

    如果你检查window._val,你会看到运行代码后它等于http://some/brand/get。所以所有函数都是从同一个变量中读取和写入的,因为var _val 的作用域在执行new Function("..."); 时会丢失。

    您应该直接在对象的原型上定义函数—target

    function generateGetterAndSetter(target: Object, key: string) {
        var getMethodName = 'get' + key.charAt(0).toUpperCase() + key.slice(1);
        var setMethodName = 'set' + key.charAt(0).toUpperCase() + key.slice(1);
    
        target[getMethodName] = function() {
            return this[key];
        };
        target[setMethodName] = function(val) {
            this[key] = val;
        };
    }
    

    【讨论】:

    • 谢谢。我想多了,迷路了。这是正确的答案,效果很好!
    猜你喜欢
    • 2017-11-28
    • 1970-01-01
    • 2014-09-03
    • 2021-11-04
    • 2021-01-14
    • 2022-12-24
    • 2015-03-22
    • 2010-09-29
    • 2018-06-30
    相关资源
    最近更新 更多