最小化 JS getter/setter 样板文件

Minimizing JS getter/setter boilerplate

我正在使用一个围绕 getter/setter 方法具有大量样板的对象创建面向闭包的对象。有什么方法可以将这些属性声明为 getter/setters 而无需重复这么多?

目前我有:

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
        width = 10,
        height = 10;

    this.setWidth = function(_w)
    {
        width = _w;
    };

    this.getWidth = function()
    {
        return width;
    };

    // etc.
}

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

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

解释:JS没有"private"或"public"properties/methods。相反,public 对象 properties/methods 附加到 this,而私有 "properties" 被声明为函数变量,如上所示。

由于JS的closure逻辑,函数变量总是对class方法是可访问的,而其他组件是看不到的。

给你:

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),
    }
}

关于闭包要记住的一件事 - 您需要一个在内部函数内外的整个范围内都可以访问的变量。

这样的事情怎么样:

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)
}

... variables are not overridden properly. ... Is there a language feature for this?

语言本身不直接提供功能。但是一如既往,可以利用各种基于函数的模式,例如作为穷人帮手的闭包和工厂模块。

出于代码重用的原因,我个人会模块化一个辅助函数,该函数通常可以解决基于键值配置的 getter 和 setter 的创建。 这样一来,人们就不会仅仅专注于实施必须解决主要任务的 factory/constructor。

除了大部分怀疑有时偏执狂试图无缘无故地封装每个对象状态的体系结构之外,我在此确实提供了一个概念性的解决方案......[paranoid][Paranoiac],有点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({}));