JavaScript 函数参数:多参数还是选项对象

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

例如,我正在编写一个函数来将一个节点列表映射到一个数组:

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

我可以用这个代替:

function map(options){
...
}

其中options是一个对象:

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

哪一种是推荐的方式?在什么时候使用一种,什么时候使用另一种,有指导方针吗?

[更新]似乎有一个共识,支持选项对象,所以我想添加一个评论:为什么我想在我的情况下使用参数列表的一个原因是有一个行为与JavaScript内建数组一致。地图的方法。

56832 次浏览

我会着眼于大型javascript项目。

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

如果你需要默认参数或可选参数,对象可能会更好,因为它更灵活。但如果你不这样做,普通的函数式参数会更明确。

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

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

创建一个对象也意味着选项可以很容易地用于多个函数:

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


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


function2(options){
alert(options.fromIndex);
}

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

像许多其他人一样,我通常更喜欢将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
});

与其说这是一门科学,不如说是一门艺术,但如果我必须说出经验法则的话:

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

  • 你有四个以上的参数
  • 任何参数都是可选的
  • 你曾经查过这个函数来找出它的参数
  • 如果有人试图勒死你,同时尖叫着“啊!”

两者兼用是很好的。如果你的函数有一个或两个必选参数和一堆可选参数,将前两个参数设为必选参数,第三个参数设为可选选项散列。

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

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

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

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

选项对象在两种情况下很方便:

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

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

另一个例子是JSON.stringify。你可能想在不传递replacer函数的情况下使用space参数——那么你必须调用…, null, 4)。arguments对象可能会更好,尽管只有2个形参并不合理。

你对以下问题的评论:

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

为什么不这么做呢?(注意:这是相当原始的Javascript。通常我会使用default散列,并使用对象传入的选项来更新它。JQuery. extend。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,
});

视情况而定。

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

  • 参数列表为长(>4)。
  • 部分或所有参数是可选的,它们不依赖于某个参数 李。< / >
  • 在未来的API更新中,参数列表可能会增加。
  • 该API将从其他代码调用,API名称不清楚 足以告诉参数的含义。所以它可能需要强度 用于可读性的参数名称

参数列表的使用场景:

  • 参数列表很短(<= 4)。
  • 大多数或所有参数都是必需的。
  • 可选参数按一定顺序排列。(例如:美元。拿)
  • 很容易通过API名称来判断参数的含义。

我可能有点晚了,但我在搜索其他开发者对这个话题的看法时,偶然发现了这个帖子。

我非常不同意大多数回复者的意见,并支持“多重论点”方法。我的主要观点是,它不鼓励其他反模式,如“改变并返回参数对象”,或“将相同的参数对象传递给其他函数”。我曾经在大量滥用这种反模式的代码库中工作过,并且快速调试这种反模式的代码变得不可能。我认为这是一个非常特定于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正确性的遵守

这只是我对这个问题的看法!