web-dev-qa-db-ja.com

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

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

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

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

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

function map(options){
    ...
}

optionsはオブジェクトです:

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

推奨される方法はどれですか?どちらを使用するかについてのガイドラインはありますか?

[更新]オプションオブジェクトを支持するコンセンサスがあるように思われるので、コメントを追加したいと思います。組み込みのarray.mapメソッド。

145
Christophe

他の多くの人と同様に、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!」と叫んでいる間に誰かがあなたを絞め殺そうとした場合。
140

複数の引数は主に必須パラメーター用です。彼らには何も問題はありません。

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

オプションオブジェクトは、次の2つの場合に役立ちます。

  • 紛らわしいほど多くのパラメーターがあるので、「命名」が役立ちます。パラメーターの順序を心配する必要はありません(特に変更される可能性がある場合)
  • オプションのパラメーターがあります。オブジェクトは非常に柔軟性があり、順序付けなしで、必要なものだけを渡します(またはundefineds)。

あなたの場合、map(nodeList, callback, options)をお勧めします。 nodelistcallbackは必須です。他の3つの引数はたまにしか来ず、妥当なデフォルトがあります。

別の例は JSON.stringify です。 space関数を渡さずにreplacerパラメーターを使用したい場合は、…, null, 4)を呼び出す必要があります。引数オブジェクトの方が優れているかもしれませんが、実際には2つのパラメーターだけでは妥当ではありません。

27
Bergi

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

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

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

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

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

オブジェクトのインスタンスを作成する場合、またはオブジェクトのメソッドを呼び出す場合は、オプションオブジェクトを使用する必要があると思います。 1つまたは2つのパラメーターだけで動作し、値を返す関数の場合は、引数リストが適しています。

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

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

9
Trevor Dixon

質問に対するあなたのコメント:

私の例では、最後の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,
});
7
Izkata

場合によります。

これらの人気のあるライブラリ設計に関する私の観察に基づいて、オプションオブジェクトを使用する必要があるシナリオを以下に示します。

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

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

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

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

私はほとんどのレスポンダーに非常に反対し、「複数の議論」アプローチに反対します。私の主な論点は、「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;
}
_

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

問題に関する私の2セント!

2
ajxs

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

2
happyCoda

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

1
Ilya Sidorovich

大規模なjavascriptプロジェクトを調べます。

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

デフォルトの引数またはオプションの引数が必要な場合は、オブジェクトの方が柔軟性が高いため、おそらくオブジェクトの方が優れているでしょう。しかし、もしあなたが通常でなければ、関数の引数はより明示的です。

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

0
dm03514