3

Note: there are a lot of questions regarding getters and setters in JavaScript (see Javascript Getters And Setters, Getters and Setters in a function (javascript), Getters and Setters in JavaScript (Correct syntax ?), automatic getter and setter(with validation) in javascript, etc). Most of these questions deal with Objects, a few with Closures and rather few of them with automatically producing getters and sets. This question deals with the later.

The answer to automatic getter and setter(with validation) in javascript is perhaps the closest for what I am looking for, but not-ideal for my use case.

Currently, in my closure I have the following:

function myClosure() {
  var closureVar1, closureVar2

  function do () { ... }

  function setupSettersAndGetters() {

    var closureVariables = ['closureVar1', 'closureVar2']

    for (var i = 0; i < closuredVariables.length; i++) {

      var currentClosureVariable = closureVariable[i]
      var toEvaluateString = "                                                 \
      do." + currentClosureVariable + " = function(value){                     \
        if(!arguments.length) return " + currentClosureVariable + ";           \
        " + currentClosureVariable + " = value;                                \
        return setup;                                                          \
      };                                                                       \
      "
      eval(toEvaluateString)
    }
  }
  setupSettersAndGetters()
  return do;
}

This works exactly as I like, e.g.

var test = myClosure()
test.closureVar1(10).closureVar2(2)
test.closureVar1() + test.closureVar2() // return 12

There is just the one "tiny" problem of using eval which some people detest.

So my question is, is there a better way to handle this and allow for me to call the setter / getter by name, rather than using a string?

e.g. I could use an intermediate function such as:

test.sget('closureVar1', 10) // set closureVar1 to 10
test.sget('closureVar2') // get closureVar2

but even doing that I think I still need to use eval? and I would prefer calling the name of the variable...

Thoughts?

SumNeuron
  • 4,850
  • 5
  • 39
  • 107
  • Look into [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). – Sebastian Simon Mar 25 '18 at 08:09
  • @Xufox will do, looks useful, but understanding the js docs has always been a challenge to me... I learned JS basically via w3schools (which gives almost no technical details of JS) and by fiddling around with d3. So if you also know a place to get a good rundown of how to do JS properly, that would also be appreciated. – SumNeuron Mar 25 '18 at 08:11
  • Well, that very same documentation page, MDN, is where I would point you to. – Sebastian Simon Mar 25 '18 at 08:16
  • @Xufox alright Ill spend more time with the MDN docs :) – SumNeuron Mar 25 '18 at 08:16
  • @SumNeuron I'm not understanding your question... if you're familiar with D3, why don't you use the classic pattern? Have a look: https://jsfiddle.net/jq5t5L2t/ – Gerardo Furtado Mar 25 '18 at 08:19
  • @GerardoFurtado you can see that pattern in the evaluation string (taken from d3 source). The issue is when I have a lot of variables. Python has the property decorator to automatically generate getter and setters. If I have 20 vars, is copy-paste the same function 20 times the only answer without eval? I'll do it, but seems sub-par :P – SumNeuron Mar 25 '18 at 08:21
  • If you have 20 vars, why don't you create a function to generate the getter/setter function automatically? – Gerardo Furtado Mar 25 '18 at 08:22
  • @GerardoFurtado that is the question :) but mine (see `toEvaluateString` in post) uses evals and I do not know how to do it without eval – SumNeuron Mar 25 '18 at 08:23
  • @SumNeuron Well, there are plenty of alternatives without eval, like this: https://jsfiddle.net/xy0xwjkn/1/ – Gerardo Furtado Mar 25 '18 at 08:28
  • @GerardoFurtado that looks really nice, but how can I handle default values? also, then users (and or myself) have to know and always call the entire list of variables that need scope under the closure... – SumNeuron Mar 25 '18 at 08:30
  • @SumNeuron you're making additional questions. I'd advise to make small steps here at S.O., otherwise it will be hard for you to get a proper answer. If you want me to write an answer, just let me know. – Gerardo Furtado Mar 25 '18 at 08:33
  • @GerardoFurtado fair point, technically this question is just "without eval" and your answer does solve it. Go ahead post it here, ill +1 it. Ill give a day for others to post suggestions prior to marking an answer. Ill add a second question for no eval with default values – SumNeuron Mar 25 '18 at 08:34
  • 1
    @SumNeuron No worries. That's exactly my point: other users can improve upon already existing answers, adding features. – Gerardo Furtado Mar 25 '18 at 08:36

1 Answers1

2

You can take the arguments passed to your myClosure function...

var args = Array.prototype.slice.call(arguments);

... and use them to set up your setters and getters:

args.forEach(function(arg) {
    instance[arg] = function(d) {
        if (!arguments.length) return arg;
        arg = d;
        return instance;
    };
});

That way, you can pass how many variables you want and also avoiding eval.

Here is a demo:

function myClosure() {
  var instance = {};
  var args = Array.prototype.slice.call(arguments);
  args.forEach(function(arg) {
    instance[arg] = function(d) {
      if (!arguments.length) return arg;
      arg = d;
      return instance;
    };
  })
  return instance;
};

var test = myClosure("v1", "v2", "v3")
test.v1(16).v2(2).v3(8)
console.log(test.v1() + test.v2() + test.v3())
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
  • follow up question is also around if you want to take a crack at it https://stackoverflow.com/questions/49474370/javascript-default-arguments-in-automatic-getters-and-setters-in-closures-witho – SumNeuron Mar 25 '18 at 09:29