提问人:Bipe 提问时间:6/19/2023 最后编辑:Bipe 更新时间:6/19/2023 访问量:229
接收函数和“默认参数”键值对象并返回另一个设置了默认参数的函数的函数
Function that receives a function and a "default parameters" key-value object and returns another function with default arguments set
问:
这听起来有点复杂。这里的想法是编写方法“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() 检索参数
答:
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
要解决 再次可用的结果的问题,您可以创建具有特定参数列表的函数,也可以覆盖返回的函数的方法。defaultMethod
defaultMethod
.toString()
0赞
Bipe
6/19/2023
确实是一个很好的解决方案!谢谢你的贡献,@Bergi
1赞
trincot
6/19/2023
#2
一种解决方案可能是维护 返回的函数的注册表。在您的示例中,它将注册函数和参数名称。然后,如果使用该函数作为参数调用,则可以在注册表中找到它并了解参数名称。defaultMethod
add_
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
事实上,它解决了问题。我不知道注册表的可能性,因为我认为如果函数不是不断调用的单例,就不可能将数据保存在函数中。很高兴知道这一点。谢谢!
评论