JavaScript:没有eval的闭包中的自动getter和setter中的默认参数?

时间:2018-03-25 09:28:57

标签: javascript closures

注意:此问题是对最近提出的问题JavaScript: automatic getters and setters in closures without eval?的跟进。

该问题的要点如下:“如何在闭包中自动为范围变量提供getter和setter - 而不使用eval语句”。有海报,提供了使用eval演示如何执行此操作的代码,并且用户提供了以下answer,其中不需要eval

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

这个问题是关于如何使用上述函数设置/获取的范围变量的默认值。

如果我们只是向变量v3添加默认值,我们会得到以下结果:

function myClosure() {
  var v3 = 2
  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") // make setters/getters for all vars
test.v1(16).v2(2) // give new values to v1, v2
console.log(test.v1() + test.v2() + test.v3()) // try to add with default v3
// 18v3

我没想到。

那么如何为变量提供默认值?

注意:请构建以下实现,在初始化时生成getter / setter(允许代码作者预先定义所有应该具有getter和setter的变量)

function myClosure() {
  var instance =  function () {};
  var publicVariables =['v1', 'v2', 'v3']
  function setup() {
    var args = Array.prototype.slice.call(arguments);
    // if called with a list, use the list, otherwise use the positional arguments
    if (typeof args[0] == 'object' && args[0].length) { args = args[0] }
    args.forEach(function(arg) {
      instance[arg] = function(d) {
        if (!arguments.length) return arg;
        arg = d;
        return instance;
      };
    })
  }
  setup(publicVariables)
  // setup('v1', 'v2', 'v3')  also works 
  return instance;
}

var test = myClosure()
test.v1(16).v2(2)
console.log(test.v1() + test.v2() + test.v3())

问题:

如何使用自动getter和setter在此设置(上面的代码块)中使用默认值?

3 个答案:

答案 0 :(得分:2)

  

该问题的要点如下:“如何在闭包中自动为范围变量提供getter和setter - 而不使用eval语句”。在那里有海报,提供了代码演示如何使用eval进行操作,用户给出了一个不需要eval的答案。

不,你不能没有eval。这里不使用任何形式的eval的所有答案都不能访问范围变量,而只是普通的属性 - 或者它们创建自己的局部变量。

提供默认值非常简单:

function myClosure(...args) {
  var instance =  {v3: 2};
//                 ^^^^^ not a `var`
  for (const arg of args) {
    let val = instance[arg];
    instance[arg] = function(d) {
      if (!arguments.length) return val;
      val = d;
      return instance;
    };
  }
  return instance;
}

答案 1 :(得分:1)

你的意思是这样的:

function myClosure(...vars) {
  const instance =  {};
  vars.forEach(varArg => {
    let name = undefined;
    let value = undefined;
    if (typeof varArg == 'string')
    {
       name = varArg;
    }
    else
    {
       name = Object.keys(varArg)[0];
       value = varArg[name];
    }
 
    instance[name] = function(d) {
      if (!arguments.length) return value;
      value = d;
      return instance;
    };
  })
  return instance;
}

const test = myClosure(
  { "v1": 1 },
  "v2",
  { "v3": 3 },
);
// Print some defaults.
console.log(test.v1());
console.log(test.v2());

test.v1(16).v2(42) // give new values to v1, v2
console.log(test.v1(), test.v2(), test.v3())

代理,因为它。

function myClosure(...vars) {
  const instance = vars.reduce((obj, { name, value }) => {
    obj[name] = value;
    return obj;
  }, {});

  let proxy;
  const handler = {
    get: function(target, prop) {
      return (...args) => {
        if (args.length == 0)
          return instance[prop];

        instance[prop] = args[0];

        return proxy;
      };
    }
  };

  proxy = new Proxy(instance, handler);
  return proxy;
}

const test = myClosure(
  { name: "v1", value: 1 },
  { name: "v2" },
  { name: "v3", value: 3 }
);
// Print some defaults.
console.log(test.v1());
console.log(test.v2());
console.log(test.vNew());

test.v1(16).v2(42).vNew(50); // give new values to some variables.
console.log(test.v1(), test.v2(), test.v3(), test.vNew())

答案 2 :(得分:0)

注意:我发布自己的答案仅供参考。我不会将此标记为问题的答案。

基于 @ H.B。提供的答案,我会通过以下方式更新答案:

  1. getters和setter是在闭包本身初始化时制作的
  2. 使得getter和setter生成函数更加混乱,以允许更懒惰的变量定义
  3. 实例现在是一个函数,而不是一个对象

    function myClosure() {
    
      var instance =  function () {
          console.log(this.v1(), this.v2(), this.v3())
      };
    
      var publicVariables =[ 'v1', 'v2', {'v3': 3} ] 
    
      function setup() {
    
    
        var args = Array.prototype.slice.call(arguments);
    
        // if called with a list, use the list, 
        // otherwise use the positional arguments
    
        if (typeof args[0] == 'object' && args[0].length) { args = args[0] }
    
        args.forEach(function(arg) {
            var name, value
            if(typeof arg == 'object') {
            name = Object.keys(arg)[0]
            value = arg[name]
          } else {
            name = arg
            value = undefined
          }
    
          instance[name] = function(d) {
            if (!arguments.length) return value;
            value = d;
            return instance;
          }; // end instance function
        }) // end for each
      } // end setup
      setup(publicVariables)
      return instance; 
    }
    
    var test = myClosure().v1(10).v2(2)
    console.log(test.v1(), test.v2(), test.v3())
    test.v1(20).v3(1)
    console.log(test.v1(), test.v2(), test.v3())