多个参数与选项对象

Posted

技术标签:

【中文标题】多个参数与选项对象【英文标题】:Multiple arguments vs. options object 【发布时间】:2012-10-01 08:59:51 【问题描述】:

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

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

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

我可以改用这个:

function map(options)
    ...

其中 options 是一个对象:

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

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

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

【问题讨论】:

第二个选项给你命名参数,在我看来这是一件好事。 它们是可选参数还是必需参数? @user1689607 在我的示例中,最后三个是可选的。 因为您的最后两个参数非常相似,如果用户只传递了一个或另一个,您将永远无法真正知道是哪一个。因此,您几乎需要命名参数。但我可以理解您希望维护一个类似于本机 API 的 API。 如果你的函数做了类似的事情,在原生 API 之后建模并不是一件坏事。这一切都归结为“是什么让代码最易读”。 Array.prototype.map 有一个简单的 API,不应该让任何半经验的编码员感到困惑。 【参考方案1】:

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

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

【讨论】:

+1 有趣。你见过它在实践中的使用吗,例如在 js 库中?【参考方案2】:

我的回复可能有点晚了,但我正在寻找其他开发者对这个话题的看法,结果发现了这个帖子。

我非常不同意大多数响应者的观点,并支持“多重论点”的方法。我的主要论点是它不鼓励其他反模式,例如“变异并返回参数对象”或“将相同的参数对象传递给其他函数”。我曾在广泛滥用这种反模式的代码库中工作,并且很快就无法调试执行此操作的代码。我认为这是一个非常特定于 Javascript 的经验法则,因为 Javascript 不是强类型的,并且允许这种任意结构的对象。

我个人的看法是,开发人员在调用函数时应该明确,避免传递冗余数据,避免通过引用修改。并不是这种模式排除了编写简洁、正确的代码。我只是觉得这让您的项目更容易陷入不良的开发实践。

考虑以下糟糕的代码:

function main() 
    const x = foo(
        param1: "something",
        param2: "something else",
        param3: "more variables"
    );

    return x;


function foo(params) 
    params.param1 = "Something new";
    bar(params);
    return params;



function bar(params) 
    params.param2 = "Something else entirely";
    const y = baz(params);
    return params.param2;


function baz(params) 
    params.params3 = "Changed my mind";
    return params;

这种类型不仅需要更明确的文档来指定意图,而且还为模糊错误留下了空间。 如果开发人员在bar() 中修改param1 怎么办?您认为查看足够大小的代码库需要多长时间才能捕捉到这一点? 诚然,这个例子有点虚伪,因为它假设开发人员此时已经提交了几个反模式。但它显示了传递包含参数的对象如何允许更大的错误和歧义空间,需要更大程度的责任心和对 const 正确性的遵守。

在这个问题上只是我的两分钱!

【讨论】:

【参考方案3】:

与许多其他人一样,我通常更喜欢将 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!”时试图勒死你

【讨论】:

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

这取决于。

根据我对那些流行的库设计的观察,以下是我们应该使用选项对象的场景:

参数列表很长 (>4)。 部分或全部参数是可选的,它们不依赖于特定的 订购。 参数列表可能会在未来的 API 更新中增加。 API会被其他代码调用,API名称不清楚 足以说明参数的含义。所以它可能需要强大的 便于阅读的参数名称。

以及使用参数列表的场景:

参数列表很短( 大部分或所有参数都是必需的。 可选参数按一定顺序排列。 (即:$.get) 很容易通过 API 名称来判断参数的含义。

【讨论】:

【参考方案5】:

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

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

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

您的参数太多以至于令人困惑:“命名”会为您提供帮助,您不必担心它们的顺序(尤其是它们可能会更改时) 您有可选参数。这些对象非常灵活,无需任何排序,您只需传递您需要的东西即可(或undefineds)。

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

另一个例子是JSON.stringify。您可能想使用space 参数而不传递replacer 函数——那么您必须调用…, null, 4)。一个 arguments 对象可能会更好,尽管它仅适用于 2 个参数并不合理。

【讨论】:

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

您对该问题的评论:

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

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

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 的回答。【参考方案7】:

对于通常使用一些预定义参数的函数,您最好使用选项对象。相反的例子就像一个函数,它得到无限数量的参数,例如:setCSS(height:100,width:200,background:"#000")。

【讨论】:

【参考方案8】:

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

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

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


function1(options)
    alert(options.nodeList);


function2(options)
    alert(options.fromIndex);

【讨论】:

这里的(合理的)假设是对象将始终具有相同的密钥对。如果您使用的是垃圾/不一致的 API,那么您将面临不同的问题。【参考方案9】:

我会看大型 javascript 项目。

你会经常看到像谷歌地图这样的东西,实例化的对象需要一个对象,但函数需要参数。我认为这与 OPTION 参数有关。

如果您需要默认参数或可选参数,对象可能会更好,因为它更灵活。但是如果你不正常的函数参数更明确。

Javascript 也有一个 arguments 对象。 https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments

【讨论】:

【参考方案10】:

Object 更可取,因为如果您传递一个对象,它很容易扩展该对象中的属性数量,并且您不必注意传递参数的顺序。

【讨论】:

以上是关于多个参数与选项对象的主要内容,如果未能解决你的问题,请参考以下文章

2自学——Linux的学习进度与任务获取命令的使用帮助

linux redhat 红帽 centos 命令格式

Vue2 element selection组件设置默认选项

Linux实验二总结

使用 getopts (bash) 的多个选项参数

使用多个参数解析命令行选项 [getopt?]