Javascript 如何将一个options对象作为参数而不是几个不同的参数来处理函数?

Javascript 如何将一个options对象作为参数而不是几个不同的参数来处理函数?,javascript,functional-programming,ecmascript-6,currying,ramda.js,Javascript,Functional Programming,Ecmascript 6,Currying,Ramda.js,在过去的几天里,我一直在研究部分申请和咖喱 我想知道如何将这些概念与只接受一个选项对象作为参数的函数结合使用 const myFunc = options => { const options.option1 = options.option1 || 'default value'; const options.option2 = options.option2 || 'another default value'; // ... etc, it takes about 5 m

在过去的几天里,我一直在研究部分申请和咖喱

我想知道如何将这些概念与只接受一个
选项
对象作为参数的函数结合使用

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…
实例化对象;我有一种预感,这可以通过部分应用来实现。当需要进行测试或实例化时,它使事情变得更简单

但是,如果没有单独的参数,我不知道如何正确地执行部分应用程序


您将如何处理此重构?

我建议将函数转换为option对象的等效方法与处理默认值的方法相同。将此视为部分应用程序:

myFuncWithMyOptions(myFunc, myOptions) {
  return function(options) {
    return myFunc(Object.assign({}, myOptions, options));
  }
}

如果您希望
myOptions
中的选项不会被
options
中的选项覆盖,只需将这两个参数交换到
对象。assign

我建议将函数转换为option对象的等效方法与处理默认值的方法相同。将此视为部分应用程序:

myFuncWithMyOptions(myFunc, myOptions) {
  return function(options) {
    return myFunc(Object.assign({}, myOptions, options));
  }
}

如果您希望
myOptions
中的选项不被
options
中的选项覆盖,只需将两个参数交换到
对象。根据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"

根据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"

我不认为你的问题与部分申请有关。
myFunc
实际上做什么

  • 它设置了两个可选的默认值
  • 它调用另一个函数
这并不多。然而,出现了两个问题:

  • 函数组合是硬编码的,隐藏在
    myFunc
  • 从函数签名中看不出哪些默认值被覆盖
简言之,“适当”函数通过其签名显示其功能。因此,让我们去掉
myFunc
包装器:

const选项={
傅:1,,
酒吧:是的,
蝙蝠:“,
baz:[]
};
//功能组合
常数comp=(…fs)=>x=>fs.reduceRight((acc,f)=>f(acc,x);
//右侧部分涂抹的涂抹器
常数$=(f,y)=>x=>f(x)(y);//右侧部分
//`Object`赋值
const assign=o=>p=>Object.assign({},o,p);
//域的特定功能
const doSomething=o=>(console.log(o),o);
//并运行(从右到左)

comp(doSomething,$(assign,{baz:[1,2,3],bat:“abc”}))(选项)我认为您的问题与部分应用程序无关。
myFunc
实际上做什么

  • 它设置了两个可选的默认值
  • 它调用另一个函数
这并不多。然而,出现了两个问题:

  • 函数组合是硬编码的,隐藏在
    myFunc
  • 从函数签名中看不出哪些默认值被覆盖
简言之,“适当”函数通过其签名显示其功能。因此,让我们去掉
myFunc
包装器:

const选项={
傅:1,,
酒吧:是的,
蝙蝠:“,
baz:[]
};
//功能组合
常数comp=(…fs)=>x=>fs.reduceRight((acc,f)=>f(acc,x);
//右侧部分涂抹的涂抹器
常数$=(f,y)=>x=>f(x)(y);//右侧部分
//`Object`赋值
const assign=o=>p=>Object.assign({},o,p);
//域的特定功能
const doSomething=o=>(console.log(o),o);
//并运行(从右到左)

comp(doSomething,$(assign,{baz:[1,2,3],bat:“abc”}))(选项)如果你只有一个参数,那么就没有什么好咖喱的了…请看@elclanrs:绝对。这是部分应用程序的另一种形式,可能很有趣。它是一个围绕请求的包装器,它本身通过其options对象接受大量选项。如果只有一个参数,那么就没有什么好咖喱的了……请参阅@elclanrs:absolute。这是部分应用程序的另一种形式,可能很有趣。它是一种围绕请求的包装器,它本身通过其options对象接受大量选项。如果有一些选项属性没有默认值,因此是必需的,您可以通过提供所需属性的名称列表来扩展它,或者将它们包含在
myOptions
中,如果发送的对象不完整,则使用一个值表示在没有它们的情况下进行的调用不完整(
undefined
,如果该值永远不是该值,则可以使用该值,或者使用附加到
myFuncWithMyOptions
的一些
符号
),您可以返回原始函数的新包装版本,其中包含一组扩展的默认值和一组减少的必填字段。@ScottSauyet是的,这可能会使调试更容易,因为调用可能会在较早时失败。但它不会改变它的效果。我想这会像其他形式的局部应用一样起作用。如果您没有提供所有必需的参数,只需返回一个新函数,该函数将把它的选项与以前的选项(以及默认值)合并,并用它们调用原始函数(假设您现在提供了所有必需的参数)。@DanD。我想斯科特指的是咖喱。要咖喱一个普通的函数,你需要知道它的算术,要咖喱一个选项对象函数,你需要知道哪个