187

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

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

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

我可以改用这个:

function map(options){
    ...
}

其中 options 是一个对象:

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

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

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

4

10 回答 10

194

像许多其他人一样,我通常更喜欢将 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!”时试图勒死你。
于 2012-10-10T19:47:36.107 回答
34

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

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

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

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

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

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

于 2012-10-10T19:56:42.477 回答
12

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

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

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

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

function2(options){
    alert(options.fromIndex);
}
于 2012-10-10T19:37:05.233 回答
11

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

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

于 2012-10-10T19:55:23.687 回答
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;
}

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

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,
});
于 2012-10-11T02:47:16.590 回答
7

我的回复可能有点晚了,但我正在寻找其他开发人员对这个主题的意见,并遇到了这个线程。

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

Not only does this kind of require more explicit documentation to specify intent, but it also leaves room for vague errors. What if a developer modifies param1 in bar()? How long do you think it would take looking through a codebase of sufficident size to catch this? Admittedly, this is example is slightly disingenuous because it assumes developers have already committed several anti-patterns by this point. But it shows how passing objects containing parameters allows greater room for error and ambiguity, requiring a greater degree of conscientiousness and observance of const correctness.

Just my two-cents on the issue!

于 2018-04-20T00:29:44.083 回答
4

这取决于。

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

  • 参数列表很长 (>4)。
  • 部分或全部参数是可选的,它们不依赖于特定顺序。
  • 参数列表可能会在未来的 API 更新中增加。
  • API 会被其他代码调用,API 名称不够清楚,无法说明参数的含义。因此,它可能需要强参数名称以提高可读性。

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

  • 参数列表很短(<= 4)。
  • 大部分或所有参数都是必需的。
  • 可选参数按一定顺序排列。(即: $.get )
  • 通过 API 名称很容易分辨参数的含义。
于 2013-04-15T19:31:55.667 回答
2

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

于 2012-10-10T19:37:10.073 回答
1

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

于 2012-10-10T19:56:10.900 回答
0

我会看大型 javascript 项目。

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

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

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

于 2012-10-10T19:36:53.143 回答