複数の引数とオプションオブジェクト


157

複数の引数を持つJavaScript関数を作成するとき、常にこの選択に直面します。引数のリストを渡すか、オプションオブジェクトを渡すかです。

たとえば、nodeListを配列にマップする関数を書いています。

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

代わりにこれを使用できます:

function map(options){
    ...
}

ここで、optionsはオブジェクトです。

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

どちらがお勧めの方法ですか?どちらを使用するかについてのガイドラインはありますか?

[更新] optionsオブジェクトを支持するコンセンサスがあるようですので、コメントを追加したいと思います。私の場合に引数のリストを使用したくなった1つの理由は、JavaScriptと一貫した動作をすることでした組み込みのarray.mapメソッド。


2
2番目のオプションは名前付き引数を提供しますが、これは私の意見では良いことです。
Werner KvalemVesterås、2012年

それらはオプションまたは必須の引数ですか?
私はレイジーが

私の例では@ user1689607の最後の3つはオプションです。
Christophe

最後の2つの引数は非常に似ているため、ユーザーがどちらか一方しか渡さなかった場合、どちらが意図されているかを実際に知ることはできません。そのため、ほとんど名前付き引数が必要になります。しかし、ネイティブAPIと同様のAPIを維持したいと思うことは認められます。
私はレイジーが

1
関数が同様の処理を行う場合は、ネイティブAPIを基にしたモデリングは悪いことではありません。すべては、「コードを最も読みやすくするもの」に帰着します。 Array.prototype.map半経験のあるコーダーを困惑させてはならない単純なAPIがあります。
ジェレミーJスターチャー

回答:


160

他の多くの人と同じように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
 });

それは科学というより芸術ですが、経験則に名前を付けなければならない場合:

次の場合は、オプションパラメータを使用します。

  • 4つ以上のパラメーターがあります
  • パラメータはいずれもオプションです
  • 関数を調べて、それが取るパラメーターを把握する必要がありました。
  • 「ARRRRRG!」と叫びながら誰かが首を絞めようとした場合。

10
すばらしい答えです。場合によります。ブールトラップに注意するariya.ofilabs.com/2011/08/hall-of-api-shame-boolean-trap.html
Trevor Dixon

2
ああそうだ...そのリンクを忘れていた。APIのしくみを再考し、私は馬鹿げたことを学んだ後、いくつかのコードを書き直しました。ありがとう!
ジェレミーJスターチャー

1
「関数がどのパラメーターをとっているかを調べるためにこれまで調べなければなりませんでした」-代わりにオプションオブジェクトを使用して、常にメソッドを調べて、キーが必要であることとそれらが呼び出されることを理解する必要はありません。IDEのIntellisenseはこの情報を表示しませんが、パラメーターは表示します。ほとんどのIDEでは、メソッドの上にマウスを置くだけで、パラメーターが何であるかが表示されます。
シンボロ2015年

1
:y'allのは、「ベストプラクティス」コードのこのビットのパフォーマンスへの影響に興味を持っている場合は、ここでjsPerfは両方の方法をテストしますjsperf.com/function-boolean-arguments-vs-options-object/10 注意に少しひねり3番目の代替方法では、「プリセット」(定数)オプションオブジェクトを使用します。これは、(ランタイムの存続期間中、Webページなどの)多くの呼び出しが、開発時に既知の同じ設定で(つまり、Webページの存続期間中に)行われたときに実行できます。 :オプション値がソースコードにハードコードされている場合)。
Ger Hobbelt 2015

2
@Sean正直なところ、私はこのスタイルのコーディングをまったく使用していません。TypeScriptに切り替えて、名前付きパラメーターを使用しました。
ジェレミーJスターチャー

28

複数の引数は、主に必須パラメーター用です。彼らには何も悪いことはありません。

オプションのパラメーターがある場合、複雑になります。そのうちの1つが他のものに依存していて、それらに特定の順序がある場合(たとえば、4番目のものには3番目のものが必要です)、引き続き複数の引数を使用する必要があります。ほぼすべてのネイティブEcmaScriptとDOMメソッドはこのように機能します。良い例はopenXMLHTTPrequestsメソッドで、最後の3つの引数はオプションです-ルールは「ユーザーなしではパスワードなし」のようなものです(MDNドキュメントも参照)。

オプションオブジェクトは、次の2つの場合に便利です。

  • パラメータが多すぎるため、混乱を招きます。「名前付け」が役立つので、パラメータの順序を気にする必要はありません(特に変更される可能性がある場合)。
  • オプションのパラメーターがあります。オブジェクトは非常に柔軟であり、順序付けなしで、必要なものだけを渡し、それ以外は何も渡しません(またはundefineds)。

あなたの場合、私はお勧めしmap(nodeList, callback, options)ます。nodelistそして、callback要求され、他の三つの引数は、たまにしか来て、合理的なデフォルト値を持っています。

別の例はJSON.stringifyです。関数spaceを渡さずにパラメータを使用したいreplacer場合は、呼び出す必要があります…, null, 4)。引数オブジェクトの方が良かったかもしれませんが、2つのパラメーターに対してはそれほど合理的ではありません。


+1 @ trevor-dixonと同じ質問:このミックスがjsライブラリなどで実際に使用されているのを見ましたか?
クリストフ

例としては、jQueryのajaxメソッドがあります。最初の引数として[必須] URLを受け入れ、2番目の引数として巨大なオプションを受け入れます。
Bergi、2012年

とても奇妙!これに気づいたことがありません。オプションのプロパティとしてurlを使用するのを見てきました...
Christophe

はい、jQueryは、下位互換性を維持しながら、オプションのパラメーターで奇妙なことを行います:-)
Bergi

1
私の意見では、これがここでの唯一の正気な答えです。
Benjamin Gruenbaum 2014年

11

「オブジェクトとしてのオプション」アプローチを使用するのが最適です。プロパティの順序を気にする必要はありません。渡されるデータには柔軟性があります(オプションのパラメーターなど)。

オブジェクトを作成すると、オプションを複数の関数で簡単に使用できるようになります。

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

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

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

ここでの(妥当な)仮定は、オブジェクトが常に同じキーペアを持つことです。がらくたや一貫性のないAPIを使用している場合は、別の問題が発生しています。
バックデスク2017年

9

何かをインスタンス化したり、オブジェクトのメソッドを呼び出したりする場合は、オプションオブジェクトを使用したいと思います。1つまたは2つのパラメーターのみを操作して値を返す関数の場合は、引数リストをお勧めします。

場合によっては、両方を使用するとよいでしょう。関数に1つまたは2つの必須パラメーターと一連のオプションのパラメーターがある場合、最初の2つのパラメーターを必須にし、3番目のパラメーターをオプションのオプションハッシュにします。

あなたの例では、私はやりますmap(nodeList, callback, options)。ノードリストとコールバックが必要です。呼び出しを読み取るだけで、何が起きているかを簡単に知ることができます。これは、既存のマップ関数のようなものです。その他のオプションは、オプションの3番目のパラメーターとして渡すことができます。


面白い+1。jsライブラリなどで実際に使用されているのを見たことがありますか?
クリストフ

7

質問に対するコメント:

私の例では、最後の3つはオプションです。

なぜこれをしないのですか? (注:これはかなり未加工のJavascriptです。通常、defaultハッシュを使用し、Object.extendまたはJQuery.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の回答に似ています。
クリストフ

5

私はこの反応でパーティーに少し遅れるかもしれませんが、このトピックに関する他の開発者の意見を探していて、このスレッドに出くわしました。

私はほとんどのレスポンダーに全く同意せず、「複数の引数」アプローチに賛成です。私の主な主張は、「paramオブジェクトを変更して返す」、「同じparamオブジェクトを他の関数に渡す」など、他のアンチパターンを阻止することです。私はこのアンチパターンを広範囲に悪用したコードベースで働いており、これを行うコードのデバッグはすぐに不可能になります。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;
}

この種の目的は、意図を指定するためにより明確なドキュメントを必要とするだけでなく、あいまいなエラーの余地も残します。開発者が変更param1した場合はどうなりbar()ますか?これを見つけるのに十分なサイズのコードベースを調べるのにどれくらい時間がかかると思いますか?確かに、これは開発者がすでにいくつかのアンチパターンをこの時点でコミットしていると想定しているため、これは少し不誠実な例です。しかし、パラメーターを含むオブジェクトを渡すことで、エラーとあいまいさの余地が大きくなり、constの正確さの良識と遵守が必要になることを示しています。

問題についてちょうど私の2セント!


3

場合によります。

これらの一般的なライブラリの設計に関する私の観察に基づいて、以下はオプションオブジェクトを使用する必要があるシナリオです。

  • パラメータリストが長い(> 4)。
  • 一部またはすべてのパラメーターはオプションであり、特定の順序に依存しません。
  • パラメータリストは、今後のAPIアップデートで大きくなる可能性があります。
  • APIは他のコードから呼び出され、API名はパラメータの意味を伝えるのに十分明確ではありません。したがって、読みやすいように厳密なパラメータ名が必要になる場合があります。

パラメータリストを使用するシナリオ:

  • パラメータリストが短い(<= 4)。
  • ほとんどまたはすべてのパラメーターが必要です。
  • オプションのパラメーターは特定の順序になっています。(つまり:$ .get)
  • API名によってパラメーターの意味を簡単に識別できます。

2

オブジェクトを渡すと、オブジェクト内のプロパティの数を簡単に拡張でき、引数が渡された順序を監視する必要がないため、オブジェクトの方が適しています。


1

通常、いくつかの事前定義された引数を使用する関数では、オプションオブジェクトを使用することをお勧めします。反対の例は、setCSS({height:100}、{width:200}、{background: "#000"})のような無数の引数を取得する関数のようなものです。


0

大規模なJavaScriptプロジェクトを見てみます。

Googleマップのようなものでは、インスタンス化されたオブジェクトにはオブジェクトが必要ですが、関数にはパラメーターが必要であることがよくわかります。これはOPTION引数と関係があると思います。

デフォルトの引数またはオプションの引数が必要な場合は、オブジェクトの方が柔軟性が高いため、オブジェクトの方が良いでしょう。ただし、通常の関数引数を使用しない場合はより明確になります。

JavaScriptにもargumentsオブジェクトがあります。 https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.