多个参数与选项对象

Chr*_*phe 148 javascript json arguments function object

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

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

function map(nodeList, callback, thisObject, fromIndex, toIndex){
    ...
}
Run Code Online (Sandbox Code Playgroud)

我可以改用它:

function map(options){
    ...
}
Run Code Online (Sandbox Code Playgroud)

options是一个对象:

options={
    nodeList:...,
    callback:...,
    thisObject:...,
    fromIndex:...,
    toIndex:...
}
Run Code Online (Sandbox Code Playgroud)

推荐哪种方式?是否有何时使用其中一种?

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

Jer*_*her 147

像许多其他人一样,我经常更喜欢传递options object一个函数而不是传递一长串参数,但它实际上取决于确切的上下文.

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

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

checkStringLength(inputStr, 10);
Run Code Online (Sandbox Code Playgroud)

我认为代码是可读的,并且传递单个参数就好了.

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

initiateTransferProtocol("http", false, 150, 90, null, true, 18);
Run Code Online (Sandbox Code Playgroud)

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

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

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

在以下情况下使用options参数:

  • 您有四个以上的参数
  • 任何参数都是可选的
  • 您必须查找函数以确定所需的参数
  • 如果有人试图扼杀你,同时尖叫"ARRRRRG!"

  • 很好的答案.这取决于.当心布尔陷阱http://ariya.ofilabs.com/2011/08/hall-of-api-shame-boolean-trap.html (9认同)
  • 哦,是的...我忘记了那个链接。这确实让我重新考虑了API的工作原理,甚至在得知我做过一些蠢事之后甚至重写了几段代码。谢谢! (2认同)
  • @Sean 老实说,我根本不再使用这种编码风格。我已经切换到 TypeScript 并使用命名参数。 (2认同)

Ber*_*rgi 28

多个参数主要用于强制性参数.他们没有错.

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

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

  • 你有这么多参数让它变得混乱:"命名"会帮助你,你不必担心它们的顺序(特别是如果它们可能改变)
  • 你有可选参数.这些对象非常灵活,没有任何顺序,你只需要传递你需要的东西而不是其他东西(或者undefineds).

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

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


Flu*_*yte 11

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

创建对象也意味着可以在多个函数上轻松使用这些选项:

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

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

function2(options){
    alert(options.fromIndex);
}
Run Code Online (Sandbox Code Playgroud)


ajx*_*jxs 10

我的回答可能有点晚了,但我正在寻找其他开发人员对这个主题的看法,并发现了这个帖子。

我非常不同意大多数回应者的观点,并支持“多重论点”的方法。我的主要论点是它不鼓励其他反模式,例如“改变并返回参数对象”或“将相同的参数对象传递给其他函数”。我曾在广泛滥用这种反模式的代码库中工作过,并且调试执行此操作的代码很快变得不可能。我认为这是一个非常特定于 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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


Tre*_*xon 9

我想如果你要实例化某个东西或者调用一个对象的方法,你想要使用一个options对象.如果它是仅对一个或两个参数进行操作并返回值的函数,则最好使用参数列表.

在某些情况下,使用两者都很好.如果您的函数有一个或两个必需参数和一堆可选参数,则需要前两个参数,第三个是可选选项哈希.

在你的例子中,我会这样做map(nodeList, callback, options).需要节点列表和回调,只需通过读取它就可以很容易地判断发生了什么,就像现有的地图功能一样.任何其他选项都可以作为可选的第三个参数传递.


Izk*_*ata 7

您对该问题的评论:

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

那么为什么不这样做呢? (注意:这是相当原始的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;
}
Run Code Online (Sandbox Code Playgroud)

所以,现在因为现在更明显的是什么是可选的,什么不是,所有这些都是函数的有效用途:

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,
});
Run Code Online (Sandbox Code Playgroud)