Javascript での引数のオーバーロードには複数の側面があります。
可変引数- 異なる引数のセット (型と数量の両方) を渡すことができ、関数は渡された引数と一致する方法で動作します。
デフォルト引数- 引数が渡されない場合は、引数のデフォルト値を定義できます。
名前付き引数- 引数の順序は無関係になり、関数に渡したい引数に名前を付けるだけです。
以下は、引数処理のこれらのカテゴリのそれぞれに関するセクションです。
可変引数
JavaScript には引数や必要な数の引数の型チェックがないため、引数myFunc()
の型、存在、または数をチェックすることで、渡された引数に適応できる実装を 1 つだけ持つことができます。
jQuery は常にこれを行います。引数の一部をオプションにすることも、渡される引数に応じて関数内で分岐することもできます。
これらのタイプのオーバーロードを実装する際には、使用できるさまざまな手法がいくつかあります。
- 宣言された引数名の値が
undefined
.
- で合計数量や引数を確認できます
arguments.length
。
- 任意の引数の型を確認できます。
- 可変数の引数の場合、
arguments
疑似配列を使用して任意の引数にアクセスできますarguments[i]
。
ここではいくつかの例を示します。
obj.data()
jQueryのメソッドを見てみましょう。次の 4 つの異なる使用方法をサポートしています。
obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);
それぞれが異なる動作をトリガーし、この動的な形式のオーバーロードを使用しないと、4 つの別個の関数が必要になります。
これらすべてのオプションを英語で区別する方法を次に示します。次に、それらすべてをコードで組み合わせます。
// get the data element associated with a particular key value
obj.data("key");
に渡された最初の引数が.data()
文字列で、2 番目の引数がundefined
の場合、呼び出し元はこの形式を使用している必要があります。
// set the value associated with a particular key
obj.data("key", value);
2 番目の引数が未定義でない場合は、特定のキーの値を設定します。
// get all keys/values
obj.data();
引数が渡されない場合は、返されたオブジェクトのすべてのキー/値を返します。
// set all keys/values from the passed in object
obj.data(object);
最初の引数の型がプレーン オブジェクトの場合、そのオブジェクトからすべてのキー/値を設定します。
これらすべてを 1 つの JavaScript ロジックのセットに組み合わせる方法は次のとおりです。
// method declaration for .data()
data: function(key, value) {
if (arguments.length === 0) {
// .data()
// no args passed, return all keys/values in an object
} else if (typeof key === "string") {
// first arg is a string, look at type of second arg
if (typeof value !== "undefined") {
// .data("key", value)
// set the value for a particular key
} else {
// .data("key")
// retrieve a value for a key
}
} else if (typeof key === "object") {
// .data(object)
// set all key/value pairs from this object
} else {
// unsupported arguments passed
}
},
この手法の鍵は、受け入れたいすべての形式の引数が一意に識別可能であり、呼び出し元がどの形式を使用しているかについて混乱がないようにすることです。これには通常、引数を適切に順序付けし、引数の型と位置に十分な一意性があることを確認して、どの形式が使用されているかを常に確認できるようにする必要があります。
たとえば、3 つの文字列引数を取る関数があるとします。
obj.query("firstArg", "secondArg", "thirdArg");
3 番目の引数を簡単にオプションにすることができ、その状態を簡単に検出できます。引数は 2 番目の引数であることを意図しているか、2 番目の引数が省略されているため、2 番目の引数の場所にあるものは実際には 3 番目の引数です。
obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");
3 つの引数はすべて同じ型であるため、異なる引数の違いを見分けることができないため、呼び出し元が何を意図していたのかわかりません。この呼び出しスタイルでは、3 番目の引数のみを省略できます。2 番目の引数を省略したい場合は、null
代わりに as (またはその他の検出可能な値) を渡す必要があり、コードはそれを検出します。
obj.query("firstArg", null, "thirdArg");
オプション引数の jQuery の例を次に示します。両方の引数はオプションであり、渡されない場合はデフォルト値を取ります:
clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map( function () {
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
},
次の jQuery の例では、引数が欠落しているか、4 つの異なるオーバーロードを提供する 3 つの異なる型のいずれかである可能性があります。
html: function( value ) {
if ( value === undefined ) {
return this[0] && this[0].nodeType === 1 ?
this[0].innerHTML.replace(rinlinejQuery, "") :
null;
// See if we can take a shortcut and just use innerHTML
} else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
value = value.replace(rxhtmlTag, "<$1></$2>");
try {
for ( var i = 0, l = this.length; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
if ( this[i].nodeType === 1 ) {
jQuery.cleanData( this[i].getElementsByTagName("*") );
this[i].innerHTML = value;
}
}
// If using innerHTML throws an exception, use the fallback method
} catch(e) {
this.empty().append( value );
}
} else if ( jQuery.isFunction( value ) ) {
this.each(function(i){
var self = jQuery( this );
self.html( value.call(this, i, self.html()) );
});
} else {
this.empty().append( value );
}
return this;
},
名前付き引数
他の言語 (Python など) では、一部の引数のみを渡し、引数が渡される順序に依存しないようにする手段として、名前付き引数を渡すことができます。Javascript は、名前付き引数の機能を直接サポートしていません。その代わりに一般的に使用される設計パターンは、プロパティ/値のマップを渡すことです。これは、プロパティと値を持つオブジェクトを渡すことによって行うことができます。ES6 以降では、実際に Map オブジェクト自体を渡すことができます。
簡単な ES5 の例を次に示します。
jQuery$.ajax()
は、プロパティと値を持つ通常の Javascript オブジェクトである単一のパラメーターを渡すだけの使用方法を受け入れます。どのプロパティを渡すかによって、どの引数/オプションが ajax 呼び出しに渡されるかが決まります。一部は必須ですが、多くはオプションです。これらはオブジェクトのプロパティであるため、特定の順序はありません。実際、そのオブジェクトには 30 を超えるさまざまなプロパティを渡すことができますが、必要なのは 1 つ (url) だけです。
次に例を示します。
$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
// process result here
});
実装の内部では$.ajax()
、着信オブジェクトに渡されたプロパティを調べて、それらを名前付き引数として使用できます。これはfor (prop in obj)
、すべてのプロパティを配列に取得してからObject.keys(obj)
その配列を反復処理することによって、またはそれによって行うことができます。
この手法は、多数の引数がある場合や多数の引数がオプションである場合に、Javascript で非常に一般的に使用されます。注: これは実装関数に責任を負わせ、最小限の有効な引数のセットが存在することを確認し、不十分な引数が渡された場合に欠落しているデバッグ フィードバックを呼び出し元に提供します (おそらく、役立つエラー メッセージで例外をスローすることによって)。 .
ES6 環境では、分割を使用して、上記で渡されたオブジェクトのデフォルトのプロパティ/値を作成できます。これについては、この参照記事で詳しく説明しています。
その記事の一例を次に示します。
function selectEntries({ start=0, end=-1, step=1 } = {}) {
···
};
次に、これを次のいずれかのように呼び出すことができます。
selectEntries({start: 5});
selectEntries({start: 5, end: 10});
selectEntries({start: 5, end: 10, step: 2});
selectEntries({step: 3});
selectEntries();
関数呼び出しでリストしない引数は、関数宣言からデフォルト値を取得します。
これにより、関数に渡されるオブジェクトのstart
、end
およびプロパティの既定のプロパティと値が作成されます。step
selectEntries()
関数の引数のデフォルト値
ES6 では、Javascript は、引数のデフォルト値の組み込み言語サポートを追加します。
例えば:
function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5
これを使用する方法の詳細については、MDN を参照してください。