过去几天我一直在研究部分申请和讨论。
我想知道如何将这些概念用于只使用一个options
对象作为参数的函数。
const myFunc = options => {
const options.option1 = options.option1 || 'default value';
const options.option2 = options.option2 || 'another default value';
// ... etc, it takes about 5 more options, all of which have a
// default fall-back value if not supplied
return doSometing(options);
}
在这种情况下,我感觉不太好改变myFunc
签名并将每个选项作为单独的参数传递,因为记住必须提供选项的顺序是一件痛苦的事。
我想坚持使用功能样式并避免使用new ...
实例化对象以保持状态;我有预感,这可以通过部分应用来实现。它可以在测试或实例化时保持简单。
但是,如果没有单独的参数,我不知道如何正确地进行部分应用。
你将如何处理这个重构?
答案 0 :(得分:2)
我建议相当于使用选项对象来调用函数与如何处理默认值相同。将此视为部分应用程序:
myFuncWithMyOptions(myFunc, myOptions) {
return function(options) {
return myFunc(Object.assign({}, myOptions, options));
}
}
如果您希望myOptions
中的选项不被options
中的选项覆盖,只需将两个参数交换为Object.assign
答案 1 :(得分:2)
根据Dan D的回答和评论,这项技术可以让你重复部分应用这样的功能,直到提供了所有必需的字段:
const vals = (obj) => Object.keys(obj).map(key => obj[key]);
const addDefaults = (() => {
const req = Symbol();
const addDefaults = (defaults, fn) => (options) => {
const params = Object.assign({}, defaults, options);
return (vals(params).includes(req))
? addDefaults(params, fn)
: fn(params);
};
addDefaults.REQUIRED = req;
return addDefaults;
})();
const order = addDefaults({
color: addDefaults.REQUIRED,
size: addDefaults.REQUIRED,
giftWrap: false,
priority: false
}, (opts) =>
`${opts.size}, ${opts.color}, ${opts.giftWrap ? '' : 'no'} wrap, priority: ${opts.priority}`
);
order({color: 'Red', size: 'Medium'}); // "Medium, Red, no wrap, priority: false"
const orderLarge = order({size: 'Large'}); // Options -> String
orderLarge({color: 'Blue'}); // "Large, Blue, no wrap, priority: false"
答案 2 :(得分:1)
我认为您的问题与部分申请无关。 myFunc
究竟做了什么?
这并不多。然而又出现了两个问题:
myFunc
简而言之,“正确”功能通过其签名显示其功能。那么让我们摆脱myFunc
包装器:
const options = {
foo: 1,
bar: true,
bat: "",
baz: []
};
// function composition
const comp = (...fs) => x => fs.reduceRight((acc, f) => f(acc), x);
// applicator for partial application with right section
const _$ = (f, y) => x => f(x) (y); // right section
// `Object` assignment
const assign = o => p => Object.assign({}, o, p);
// specific function of your domain
const doSomething = o => (console.log(o), o);
// and run (from right-to-left)
comp(doSomething, _$(assign, {baz: [1, 2, 3], bat: "abc"})) (options);
现在,您无需查看函数体就可以准确地看到正在发生的事情。选项Object
的属性顺序也无关紧要。
关于_$
的评论。它有这个奇怪的名字,因为在这个特殊情况下,我更喜欢视觉名称而不是文字名称。鉴于函数sub = x => y => x - y
,_$(sub, 2)
仅表示x => x - 2
。这被称为部分应用的sub
函数的右侧部分,因为“left”参数仍然缺失。