最小化JS getter/setter样板文件



我正在用一个围绕getter/setter方法有大量样板的对象创建面向闭包的对象。有没有办法将这些属性声明为getter/setter而不用重复这么多?

目前我有:

var test = function(){
    var width = 10;    
    var height = 10;    
    return {
        width: {get: function(){return width;}, set: function(_){width=_;}},
        height: {get: function(){return height;}, set: function(_){height=_;}},
    }
}

,但我想要更像:

var test = function(){
    var width = 10;    
    var height = 10;    
    return {
        width: getset(width)
        height: getset(height),
    }
}

我想写这样一个函数:

var test = function(){
    var getset = function(val){
        return {
            get: function(){
                console.log('getting');
                return val
            },
            set: function(_){
                console.log('besetted');
                param=_
            },
        }
    }
    var width = 10;    
    var height = 10;    
    return {
        width: getset(width),
        height: getset(height),
    }
}

,但这不起作用;变量没有被正确地覆盖。最好的方法是什么?是否有相应的语言特性?

给你:

var test = function(){
    var getset = function(value){
        var val = value;
        return {
            get: function(){
                console.log('getting');
                return val
            },
            set: function(_){
                console.log('besetted');
                val=_
            },
        }
    }
    var width = 10;
    var height = 10;
    return {
        width: getset(width),
        height: getset(height),
    }
}

关于闭包要记住的一件事——你需要一个变量,这个变量既可以在内部函数的作用域内访问,也可以在外部访问。

你想要的是:

var test = function()
{
    var
        width = 10,
        height = 10;
    this.setWidth = function(_w)
    {
        width = _w;
    };
    this.getWidth = function()
    {
        return width;
    };
    // etc.
}

现在你有了一个可以实例化的"类":

var testObj = new test();
console.log(testObj.getWidth()); // 10
testObj.setWidth(20);
console.log(testObj.getWidth()); // 20

解释:JS没有"私有"或"公共"属性/方法。相反,公共对象属性/方法被附加到this,而私有"属性"被声明为函数变量,如上所示。

由于JS的闭包逻辑,类方法总是可以访问函数变量,而其他组件无法看到它们。

这样如何:

function GetSet(value) = {
  this.myValue = value;
  var that = this;
  this.getset = function(setter){
    if (setter === undefined){
      return that.myValue;
    } else {
      that.myValue = setter;
    }
  }
}

所以你替换:

return {
    width: getset(width),
    height: getset(height),
}

:

return {
  width: new GetSet(width),
  height: new GetSet(height)
}

…变量没有被正确覆盖. ...是否有相应的语言特性?

语言本身并不直接提供特性。但是,像往常一样,可以使用各种基于函数的模式,如闭包作为穷人的助手和工厂模块。

出于代码重用的原因,我个人会模块化一个辅助函数,该函数通常解决基于键值配置的getter和setter的创建问题。这样一来,人们就不必仅仅关注于实现工厂/构造器,而这些工厂/构造器必须解决主要任务。

除了大多数人怀疑的架构,有时偏执地试图封装每个对象状态没有很好的理由,我在此提供一个概念性的解决方案…[paranoid], [Paranoiac], kind test

var paranoid = (function () {

  var
    module,

    firstCharToUpperCase = function (str) {
      var
        list = str.split(""),
        char = list.shift()
      ;
      list.unshift(char.toUpperCase());
      return list.join("");
    },
    createKeyForSetter = function (key) {
      return ["set", firstCharToUpperCase(key)].join("");
    },
    createKeyForGetter = function (key) {
      return ["get", firstCharToUpperCase(key)].join("");
    },

    setTargetValue = function (target, key, value) {
      return (target[key] = value);
    },
    getTargetValue = function (target, key) {
      return target[key];
    },

    createAndAggregateGetterAndSetterForEachKeyValueAndTarget = function (collector, key) {
      collector.target[createKeyForSetter(key)] = function (value) {
      //return setTargetValue(collector.config, key, value);
        setTargetValue(collector.config, key, value);
      //return collector.target;
        return this;
      };
      collector.target[createKeyForGetter(key)] = function () {
        return getTargetValue(collector.config, key);
      };
      return collector;
    }
  ;

  module = {
    aggregateGettersAndSetters: createAndAggregateGetterAndSetterForEachKeyValueAndTarget
  };

  return module;

}());

var Paranoiac = (function (global) {

  var
    Constructor,
    factory,

    object_keys = global.Object.keys,

    createAndAggregateGetterAndSetterForEachKeyValueAndTarget = global.paranoid.aggregateGettersAndSetters,

    createInstanceFromConfig = function (config) {
      var instance = null;
      if ((config != null) && (object_keys(config).length >= 1))  {
        instance = new Constructor(config);
      }
      return instance;
    },
    isInstance = function (type) {
      return (
           (type != null)
        && (type instanceof Constructor)
      );
    }
  ;

  Constructor = function Paranoiac (config) {
    var
      instance = this
    ;
    object_keys(config).reduce(createAndAggregateGetterAndSetterForEachKeyValueAndTarget, {
      config: config,
      target: instance
    });
    return instance;
  };

  factory = {
    create      : createInstanceFromConfig,
    isParanoiac : isInstance
  };

  return factory;

}(window || this));

var
  p1 = Paranoiac.create({width: 55, height: 66}),
  p2 = Paranoiac.create({width: 44, height: 77}),
  p3 = Paranoiac.create({width: 33, height: 99})
;
console.log("Object.keys(p1) - ", Object.keys(p1));
console.log("Object.keys(p2) - ", Object.keys(p2));
console.log("Object.keys(p3) - ", Object.keys(p3));
console.log("p1.getWidth(), p1.getHeight() - ", [p1.getWidth(), p1.getHeight()]);
console.log("p2.getWidth(), p2.getHeight() - ", [p2.getWidth(), p2.getHeight()]);
console.log("p3.getWidth(), p3.getHeight() - ", [p3.getWidth(), p3.getHeight()]);
console.log('p1.setWidth("5"), p1.setHeight("6") - ', [p1.setWidth("5"), p1.setHeight("6")]);
console.log("p1.getWidth(), p1.getHeight() - ", [p1.getWidth(), p1.getHeight()]);
console.log("p2.getWidth(), p2.getHeight() - ", [p2.getWidth(), p2.getHeight()]);
console.log("p3.getWidth(), p3.getHeight() - ", [p3.getWidth(), p3.getHeight()]);
console.log('p2.setWidth("4"), p2.setHeight("7") - ', [p2.setWidth("4"), p2.setHeight("7")]);
console.log('p3.setWidth("3"), p3.setHeight("9") - ', [p3.setWidth("3"), p3.setHeight("9")]);
console.log("p1.getWidth(), p1.getHeight() - ", [p1.getWidth(), p1.getHeight()]);
console.log("p2.getWidth(), p2.getHeight() - ", [p2.getWidth(), p2.getHeight()]);
console.log("p3.getWidth(), p3.getHeight() - ", [p3.getWidth(), p3.getHeight()]);
console.log("Paranoiac.isParanoiac(p1) - ", Paranoiac.isParanoiac(p1));
console.log("Paranoiac.isParanoiac(p2) - ", Paranoiac.isParanoiac(p2));
console.log("Paranoiac.isParanoiac(p3) - ", Paranoiac.isParanoiac(p3));
console.log("Paranoiac.isParanoiac() - ", Paranoiac.isParanoiac());
console.log("Paranoiac.isParanoiac({}) - ", Paranoiac.isParanoiac({}));

最新更新