多个参数与选项对象

IT技术 javascript json function object arguments
2021-03-10 04:17:02

在创建具有多个参数的 JavaScript 函数时,我总是面临这样的选择:传递参数列表与传递选项对象。

例如,我正在编写一个将 nodeList 映射到数组的函数:

function map(nodeList, callback, thisObject, fromIndex, toIndex){
    ...
}

我可以改用这个:

function map(options){
    ...
}

其中 options 是一个对象:

options={
    nodeList:...,
    callback:...,
    thisObject:...,
    fromIndex:...,
    toIndex:...
}

哪一种是推荐的方式?是否有关于何时使用一种与另一种的指南?

[更新] 似乎有一个支持 options 对象的共识,所以我想添加一个评论:我想在我的案例中使用参数列表的一个原因是具有与 JavaScript 一致的行为内置 array.map 方法。

6个回答

像许多其他人一样,我通常更喜欢将 an 传递options object给函数而不是传递一长串参数,但这实际上取决于确切的上下文。

我使用代码可读性作为试金石。

例如,如果我有这个函数调用:

checkStringLength(inputStr, 10);

我认为代码的可读性很好,传递单个参数也很好。

另一方面,有这样的调用函数:

initiateTransferProtocol("http", false, 150, 90, null, true, 18);

除非你做一些研究,否则完全不可读。另一方面,这段代码读起来很好:

initiateTransferProtocol({
  "protocol": "http",
  "sync":      false,
  "delayBetweenRetries": 150,
  "randomVarianceBetweenRetries": 90,
  "retryCallback": null,
  "log": true,
  "maxRetries": 18
 });

它更像是一门艺术而不是一门科学,但如果我必须说出经验法则:

在以下情况下使用选项参数:

  • 你有四个以上的参数
  • 任何参数都是可选的
  • 你曾经不得不查找函数来弄清楚它需要什么参数
  • 如果有人试图在尖叫“ARRRRRG!”的同时勒死你
哦,是的......我忘记了那个链接。这真的让我重新思考 API 是如何工作的,我什至在得知自己做了一些愚蠢的事情后重写了几段代码。谢谢!
2021-04-15 04:17:02
'你曾经不得不查找函数来找出它需要什么参数' - 使用选项对象,你不会总是需要查找方法来确定需要的键以及它们被调用的内容。IDE 中的 Intellisense 不会显示此信息,而 params 会显示。在大多数 IDE 中,您只需将鼠标悬停在方法上,它就会显示参数是什么。
2021-04-15 04:17:02
@Sean 老实说,我根本不再使用这种编码风格。我已经切换到 TypeScript 并使用命名参数。
2021-04-18 04:17:02
如果你们都对这一点“编码最佳实践”的性能结果感兴趣,这里有一个 jsPerf 测试两种方式:jsperf.com/function-boolean-arguments-vs-options-object/10 注意第三种选择,我使用“预设”(常量)选项对象,当您有许多调用(在运行时的生命周期内,例如您的网页)使用相同的设置时可以完成,在开发时已知(简而言之:当您的选项值在源代码中有点硬编码时)。
2021-05-07 04:17:02
很好的答案。这取决于。当心布尔陷阱ariya.ofilabs.com/2011/08/hall-of-api-shame-boolean-trap.html
2021-05-12 04:17:02

多个参数主要用于强制参数。他们没有任何问题。

如果您有可选参数,它会变得复杂。如果其中一个依赖于其他,因此它们具有特定的顺序(例如,第四个需要第三个),您仍然应该使用多个参数。几乎所有原生 EcmaScript 和 DOM 方法都是这样工作的。一个很好的例子是openXMLHTTPrequests方法,其中最后 3 个参数是可选的 - 规则就像“没有用户就没有密码”(另见MDN 文档)。

Option 对象在两种情况下派上用场:

  • 你有太多的参数让人困惑:“命名”会帮助你,你不必担心它们的顺序(特别是如果它们可能会改变)
  • 你有可选参数。这些对象非常灵活,无需任何排序,您只需传递您需要的东西,而没有其他(或undefineds)。

在你的情况下,我会推荐map(nodeList, callback, options). nodelist并且callback是必需的,其他三个参数只是偶尔出现并且具有合理的默认值。

另一个例子是JSON.stringify您可能希望在space不传递replacer函数的情况下使用参数- 然后您必须调用…, null, 4). 一个 arguments 对象可能会更好,尽管它对于只有 2 个参数并不真正合理。

太奇怪了!我以前从未注意到这一点。我一直看到它与 url 一起用作选项属性......
2021-04-21 04:17:02
是的,jQuery 用它的可选参数做了奇怪的事情,同时保持向后兼容:-)
2021-04-21 04:17:02
在我看来,这是这里唯一合理的答案。
2021-04-29 04:17:02
一个例子可能是 jQuery 的ajax 方法他们接受 [obligatory] ​​URL 作为第一个参数,并接受一个巨大的 options 参数作为第二个参数。
2021-05-01 04:17:02
+1 与@trevor-dixon 相同的问题:您是否在实践中看到过这种混合使用,例如在 js 库中?
2021-05-06 04:17:02

使用“选项作为对象”的方法将是最好的。您不必担心属性的顺序,并且可以更灵活地传递数据(例如,可选参数)

创建一个对象也意味着这些选项可以很容易地用于多个功能:

options={
    nodeList:...,
    callback:...,
    thisObject:...,
    fromIndex:...,
    toIndex:...
}

function1(options){
    alert(options.nodeList);
}

function2(options){
    alert(options.fromIndex);
}
这里的(合理的)假设是对象将始终具有相同的密钥对。如果您使用的是垃圾/不一致的 API,那么您将面临不同的问题。
2021-05-12 04:17:02

两者结合使用会很好。如果您的函数有一个或两个必需参数和一堆可选参数,请将前两个参数设为必需参数,将第三个参数设为可选选项哈希。

在你的例子中,我会做map(nodeList, callback, options). Nodelist 和回调是必需的,通过读取对它的调用就可以很容易地知道发生了什么,就像现有的地图函数一样。任何其他选项都可以作为可选的第三个参数传递。

+1 有趣。你有没有看到它在实践中使用过,例如在 js 库中?
2021-05-13 04:17:02

你对这个问题的评论:

在我的例子中,最后三个是可选的。

那么为什么不这样做呢? (注意:这是相当原始的 Javascript。通常我会使用default哈希并使用通过使用Object.extendJQuery.extend或类似方法传入的选项更新它..)

function map(nodeList, callback, options) {
   options = options || {};
   var thisObject = options.thisObject || {};
   var fromIndex = options.fromIndex || 0;
   var toIndex = options.toIndex || 0;
}

所以,既然现在什么是可选的,什么不是,现在变得更加明显,所有这些都是该函数的有效用法:

map(nodeList, callback);
map(nodeList, callback, {});
map(nodeList, callback, null);
map(nodeList, callback, {
   thisObject: {some: 'object'},
});
map(nodeList, callback, {
   toIndex: 100,
});
map(nodeList, callback, {
   thisObject: {some: 'object'},
   fromIndex: 0,
   toIndex: 100,
});
这类似于@trevor-dixon 的回答。
2021-04-23 04:17:02