接收函数和“默认参数”键值对象并返回另一个设置了默认参数的函数的函数

Function that receives a function and a "default parameters" key-value object and returns another function with default arguments set

提问人:Bipe 提问时间:6/19/2023 最后编辑:Bipe 更新时间:6/19/2023 访问量:229

问:

这听起来有点复杂。这里的想法是编写方法“defaultMethod”。它接收一个函数和一个对象。如果函数是一个简单的添加:

function add(a,b) { return a+b;};

通话时

var add_ = defaultMethod(add,{b:9});
add_(10)

行为是:

  • 默认值:undefined
  • B 的默认值:9
  • A的接收值:10
  • B的接收值:未定义

退货必须是 19。

问题是该方法可以多次调用:

var add_ = defaultMethod(add,{b:9}); // set 'b' default value as 9
    add_ = defaultMethod(add_,{b:3, a:2}); // now, set 'b' default value as 3 and 'a' as 2
    let res = add_(10) //sent 'a' value as 10
    expect(res).toBe(13); //10 (received) + 3 (default) 

我是这样写的:

function defaultMethod(func, params) {
    var funcStr = func.toString();
    let requiredArgs = funcStr
      .slice(funcStr.indexOf('(') + 1, funcStr.indexOf(')')) //get the between parenthesis part
      .match(/([^\s,]+)/g) || []; //resulting in ['a', 'b']

    console.log(requiredArgs)
  
    return function (...args) {
      let calledArgs = args;
  
      if (calledArgs.length < requiredArgs.length) {
        for (let i = calledArgs.length; i < requiredArgs.length; i++) {
          if (calledArgs[i] === undefined) {
            calledArgs[i] = params[requiredArgs[i]];
          }
        }
      }
  
      return func(...calledArgs);
    };
  }
  

它适用于一个调用,例如,所有这些单元测试都通过:

var add_ = defaultMethod(add,{b:9});

it('should return 19', () => {
    expect(add_(10)).toBe(19);
})

it('should return 17', () => {
    expect(add_(10,7)).toBe(17);
})

it('should return nan', () => {
    expect(add_()).toBe(NaN);
})

虽然,当我们再次调用 defaultMethod 时,现在传递函数,它开始中断。开始记录而不是 .add_console.log(requiredArgs)[...args]['a', 'b']

单元测试如下:

var add_ = defaultMethod(add,{b:9}); // set b default value as 9
    add_ = defaultMethod(add_,{b:3, a:2}); // now, set b default value as 3 and a as 2

it('should return 13', () => {
    expect(add_(10)).toBe(13); //10 (received) + 3 (default) 
})//this one breaks returning 19

it('should return 5', () => {
    expect(add_()).toBe(5);
})//this one breaks returning NaN

it('should return nan', () => {
    add_ = defaultMethod(add_,{c:3}); // this doesn't do anything because c isn't required
    expect(add_(10)).toBe(NaN);
})//this one breaks returning 19

而且我想不出一种方法让它为多个调用工作。显然,GPT-4 两者都不是。有什么想法吗?

编辑:我应该注意,要求是:

  • 不使用全局范围(函数外部)
  • 我们必须通过 func.toString() 检索参数
javascript 传递 default-arguments 函数参数

评论


答:

1赞 Bergi 6/19/2023 #1

不要依赖.正如你所证明的那样,有很多方法可以而且确实会打破这种局面;这不是一个普遍可以解决的问题。参数名称是实现详细信息,应被视为不可靠。func.toString()

相反,让你的函数接受一个具有命名属性的对象,你可以很容易地合并这些属性:

function add({a, b}) { return a+b; }
function defaultArgs(fn, def) { return arg => fn({...def, ...arg}); }

有了这个,你的测试用例就可以工作了:

const add9 = defaultArgs(add, {b: 9});
console.log(add9({a: 10})); // 19
console.log(add9({a: 9, b: 6})); // 15
const add32 = defaultArgs(add9, {a: 3, b: 2});
console.log(add32()); // 5
console.log(add32({b: 10})); // 13
console.log(add32({a: 2})); // 4

评论

0赞 Bipe 6/19/2023
这是一个优雅的解决方案,但对于这个练习,要求我不要像你那样将参数作为对象发送。我知道在现实世界中这是最佳实践,但在这个代码挑战中,它被要求不使用它。此外,挑战的“提示”是使用该方法来检索参数.toString()
0赞 Bergi 6/19/2023
在这种情况下,应验证函数的参数声明是否具有预期的语法,而不是在第一个右括号之前仅使用逗号进行拆分,如果参数具有未经处理的语法,则失败并显示明显的错误消息。
1赞 Bergi 6/19/2023
要解决 再次可用的结果的问题,您可以创建具有特定参数列表的函数,也可以覆盖返回的函数的方法。defaultMethoddefaultMethod.toString()
0赞 Bipe 6/19/2023
确实是一个很好的解决方案!谢谢你的贡献,@Bergi
1赞 trincot 6/19/2023 #2

一种解决方案可能是维护 返回的函数的注册表。在您的示例中,它将注册函数和参数名称。然后,如果使用函数作为参数调用,则可以在注册表中找到它并了解参数名称。defaultMethodadd_defaultMethod

所以代码会像这样改变:

function add(a,b) { return a + b; }

const defaultMethod = (function () { // Create a closure for `registry`
    const registry = new WeakMap;
    
    return function (func, params) {
        let requiredArgs = registry.get(func);
        if (!requiredArgs) {
            const funcStr = func.toString();
            requiredArgs = funcStr
              .slice(funcStr.indexOf('(') + 1, funcStr.indexOf(')'))
              .match(/([^\s,]+)/g) || [];
        }

        console.log("parameters are:", ...requiredArgs);
      
        const decoratedFunc = function (...args) {
            let calledArgs = args;
      
            for (let i = calledArgs.length; i < requiredArgs.length; i++) {
                if (calledArgs[i] === undefined) {
                    calledArgs[i] = params[requiredArgs[i]];
                }
            }
      
            return func(...calledArgs);
        };
        // Register the function we are about to return
        registry.set(decoratedFunc, requiredArgs);
        return decoratedFunc;
    };
})();
  
console.log("set defaults for add_ to {b:9}");
let add_ = defaultMethod(add,{b:9});
console.log("call add_(10): expect 19");
console.log(add_(10));
console.log("set defaults for add_ to {b:3, a:2}");
add_ = defaultMethod(add_,{b:3, a:2});
console.log("call add_(10): expect 13");
console.log(add_(10));
console.log("call add_(): expect 5");
console.log(add_());

评论

0赞 Bipe 6/19/2023
事实上,它解决了问题。我不知道注册表的可能性,因为我认为如果函数不是不断调用的单例,就不可能将数据保存在函数中。很高兴知道这一点。谢谢!