ES6の引数とパラメータの使用方法
公開: 2022-03-10ECMAScript 6(またはECMAScript 2015)は、ECMAScript標準の最新バージョンであり、JavaScriptでのパラメーター処理が大幅に改善されています。 他の新機能の中でも、RESTパラメーター、デフォルト値、デストラクチャリングを使用できるようになりました。
このチュートリアルでは、引数とパラメーターを詳細に調べ、ECMAScript6がそれらをどのようにアップグレードしたかを確認します。
引数とパラメータ
引数とパラメーターは、しばしば同じ意味で参照されます。 それでも、このチュートリアルでは、区別します。 ほとんどの標準では、パラメーター(または仮パラメーター)は関数宣言で指定されたものであり、引数(または実際のパラメーター)は関数に渡されるものです。 この関数について考えてみましょう。
function foo(param1, param2) { // do something } foo(10, 20);
この関数では、 param1
とparam2
は関数パラメーターであり、関数に渡される値( 10
と20
)は引数です。
スプレッド演算子(…)
ECMAScript 5では、 apply()
メソッドは、配列を引数として関数に渡すための便利なツールです。 たとえば、 Math.max()
メソッドで一般的に使用され、配列内の最大値を検索します。 このコードフラグメントを考えてみましょう。
var myArray = [5, 10, 50]; Math.max(myArray); // Error: NaN Math.max.apply(Math, myArray); // 50
Math.max()
メソッドは配列をサポートしていません。 数字のみを受け入れます。 配列がMath.max()
関数に渡されると、エラーがスローされます。 ただし、 apply()
メソッドを使用すると、配列は個別の数値として送信されるため、 Math.max()
メソッドで処理できます。
幸い、ECMAScript 6にspread演算子が導入されたことで、 apply()
メソッドを使用する必要がなくなりました。 スプレッド演算子を使用すると、式を複数の引数に簡単に展開できます。
var myArray = [5, 10, 50]; Math.max(...myArray); // 50
ここで、spread演算子はmyArray
を展開して、関数の個々の値を作成します。 ECMAScript 5でapply()
を使用してスプレッド演算子をシミュレートすることは可能ですが、構文は混乱を招き、スプレッド演算子の柔軟性に欠けています。 スプレッド演算子は、使いやすいだけでなく、より多くの機能を備えています。 たとえば、複数回使用したり、 function
呼び出しで他の引数と組み合わせたりすることができます。
function myFunction() { for(var i in arguments){ console.log(arguments[i]); } } var params = [10, 15]; myFunction(5, ...params, 20, ...[25]); // 5 10 15 20 25
スプレッド演算子のもう1つの利点は、コンストラクターで簡単に使用できることです。
new Date(...[2016, 5, 6]); // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)
もちろん、ECMAScript 5で前述のコードを書き直すことはできますが、型エラーが発生しないように、複雑なパターンを使用する必要があります。
new Date.apply(null, [2016, 4, 24]); // TypeError: Date.apply is not a constructor new (Function.prototype.bind.apply(Date, [null].concat([2016, 5, 6]))); // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)
関数呼び出しでの演算子ブラウザのサポートの拡散
デスクトップブラウザ:
クロム | Firefox | インターネットエクスプローラ | マイクロソフトエッジ | オペラ | サファリ |
---|---|---|---|---|---|
46 | 27 | – | サポートされています | – | 7.1 |
モバイルブラウザ:
Chrome for Android | Firefox Mobile | Safari Mobile | Opera Mobile | IEモバイル |
---|---|---|---|---|
46 | 27 | 8 | – | – |
残りのパラメーター
restパラメーターの構文はspread演算子と同じですが、配列をパラメーターに展開する代わりに、パラメーターを収集して配列に変換します。
function myFunction(...options) { return options; } myFunction('a', 'b', 'c'); // ["a", "b", "c"]
引数がない場合、restパラメーターは空の配列に設定されます。
function myFunction(...options) { return options; } myFunction(); // []
残りのパラメーターは、可変個引数関数(可変数の引数を受け入れる関数)を作成するときに特に役立ちます。 配列であるという利点があるため、RESTパラメーターはarguments
オブジェクトを簡単に置き換えることができます(これについては、このチュートリアルの後半で説明します)。 ECMAScript5で記述されたこの関数について考えてみます。
function checkSubstrings(string) { for (var i = 1; i < arguments.length; i++) { if (string.indexOf(arguments[i]) === -1) { return false; } } return true; } checkSubstrings('this is a string', 'is', 'this'); // true
この関数は、文字列にいくつかの部分文字列が含まれているかどうかをチェックします。 この関数の最初の問題は、 function
の本体の内部を調べて、複数の引数を取る必要があることです。 2番目の問題は、 arguments[0]
が最初の引数を指しているため、反復は0
ではなく1
から開始する必要があることです。 後で文字列の前後に別のパラメータを追加することにした場合、ループの更新を忘れる可能性があります。 残りのパラメーターを使用すると、次の問題を簡単に回避できます。
function checkSubstrings(string, ...keys) { for (var key of keys) { if (string.indexOf(key) === -1) { return false; } } return true; } checkSubstrings('this is a string', 'is', 'this'); // true
この関数の出力は前のものと同じです。 ここでも、パラメータstring
は最初に渡される引数で埋められますが、残りの引数は配列に入れられ、変数keys
に割り当てられます。
arguments
オブジェクトの代わりにrestパラメーターを使用すると、コードの可読性が向上し、JavaScriptでの最適化の問題が回避されます。 それにもかかわらず、restパラメーターには制限があります。 たとえば、これは最後の引数である必要があります。 そうしないと、構文エラーが発生します。
function logArguments(a, ...params, b) { console.log(a, params, b); } logArguments(5, 10, 15); // SyntaxError: parameter after rest parameter
もう1つの制限は、 function
宣言で許可されるRESTパラメーターは1つだけであるということです。
function logArguments(...param1, ...param2) { } logArguments(5, 10, 15); // SyntaxError: parameter after rest parameter
残りのパラメーターブラウザのサポート
デスクトップブラウザ:
クロム | Firefox | インターネットエクスプローラ | マイクロソフトエッジ | オペラ | サファリ |
---|---|---|---|---|---|
47 | 15 | – | サポートされています | 34 | – |
モバイルブラウザ:
Chrome for Android | Firefox Mobile | Safari Mobile | Opera Mobile | IEモバイル |
---|---|---|---|---|
47 | 15 | – | – | – |
デフォルトパラメータ
ECMAScript5のデフォルトパラメータ
JavaScriptはECMAScript5のデフォルトのパラメーターをサポートしていませんが、簡単な回避策があります。 関数内で論理OR
演算子( ||
)を使用すると、ECMAScript5のデフォルトパラメーターを簡単にシミュレートできます。次の関数について考えてみます。
function foo(param1, param2) { param1 = param1 || 10; param2 = param2 || 10; console.log(param1, param2); } foo(5, 5); // 5 5 foo(5); // 5 10 foo(); // 10 10
この関数は2つの引数を想定していますが、引数なしで呼び出されると、デフォルト値が使用されます。 関数内では、欠落している引数は自動的に未定義に設定されます。 したがって、これらの引数を検出して、それらのデフォルト値を宣言できます。 欠落している引数を検出してデフォルト値を設定するには、論理OR
演算子( ||
)を使用します。 この演算子は最初の引数を調べます。それが真実である場合、演算子はそれを返します。 そうでない場合、演算子は2番目の引数を返します。
このアプローチは関数で一般的に使用されますが、欠点があります。 0
またはnull
を渡すと、デフォルト値もトリガーされます。これは、これらが偽の値と見なされるためです。 したがって、実際にこの関数に0
またはnull
を渡す必要がある場合は、引数が欠落しているかどうかを確認する別の方法が必要になります。
function foo(param1, param2) { if(param1 === undefined){ param1 = 10; } if(param2 === undefined){ param2 = 10; } console.log(param1, param2); } foo(0, null); // 0, null foo(); // 10, 10
この関数内では、渡された引数のタイプがチェックされ、デフォルト値が割り当てられる前にそれらが未定義であることを確認します。 このアプローチにはもう少し多くのコードが必要ですが、より安全な代替手段であり、関数に0
とnull
を渡すことができます。
ECMAScript6のデフォルトパラメータ
ECMAScript 6では、デフォルトのパラメーターをシミュレートするために未定義の値をチェックする必要がなくなりました。 これで、 function
宣言にデフォルト値を正しく入れることができます。
function foo(a = 10, b = 10) { console.log(a, b); } foo(5); // 5 10 foo(0, null); // 0 null
ご覧のとおり、引数を省略するとデフォルト値がトリガーされますが、 0
またはnull
を渡してもトリガーされません。 関数を使用して、デフォルトパラメータの値を取得することもできます。
function getParam() { alert("getParam was called"); return 3; } function multiply(param1, param2 = getParam()) { return param1 * param2; } multiply(2, 5); // 10 multiply(2); // 6 (also displays an alert dialog)
getParam
関数は、2番目の引数が省略されている場合にのみ呼び出されることに注意してください。 したがって、2つのパラメーターを指定してmultiply()
関数を呼び出すと、アラートは表示されません。
デフォルトパラメータのもう1つの興味深い機能は、 function
宣言で他のパラメータと変数を参照できることです。
function myFunction(a=10, b=a) { console.log('a = ' + a + '; b = ' + b); } myFunction(); // a=10; b=10 myFunction(22); // a=22; b=22 myFunction(2, 4); // a=2; b=4
function
宣言で操作を実行することもできます。
function myFunction(a, b = ++a, c = a*b) { console.log(c); } myFunction(5); // 36
他のいくつかの言語とは異なり、JavaScriptは呼び出し時にデフォルトのパラメーターを評価することに注意してください。
function add(value, array = []) { array.push(value); return array; } add(5); // [5] add(6); // [6], not [5, 6]
デフォルトのパラメータブラウザのサポート
デスクトップブラウザ:
特徴 | クロム | Firefox | インターネットエクスプローラ | マイクロソフトエッジ | オペラ | サファリ |
---|---|---|---|---|---|---|
基本的なサポート | 49 | 15 | – | 14 | – | – |
デフォルトパラメータの後にデフォルトのないパラメータ | 49 | 26 | – | 14 | – | – |
モバイルブラウザ:
特徴 | Chrome for Android | Firefox Mobile | Safari Mobile | Opera Mobile | IEモバイル |
---|---|---|---|---|---|
基本的なサポート | 49 | 15 | – | – | – |
デフォルトパラメータの後にデフォルトのないパラメータ | 46 | 26 | – | – | – |
破壊
デストラクチャリングはECMAScript6の新機能であり、配列とオブジェクトから値を抽出し、オブジェクトと配列のリテラルに似た構文を使用してそれらを変数に割り当てることができます。 構文は明確で理解しやすく、関数に引数を渡すときに特に役立ちます。
ECMAScript 5では、特にプロパティの順序が重要でない場合に、構成オブジェクトを使用して多数のオプションのパラメーターを処理することがよくあります。 この関数について考えてみましょう。
function initiateTransfer(options) { var protocol = options.protocol, port = options.port, delay = options.delay, retries = options.retries, timeout = options.timeout, log = options.log; // code to initiate transfer } options = { protocol: 'http', port: 800, delay: 150, retries: 10, timeout: 500, log: true }; initiateTransfer(options);
このパターンはJavaScript開発者によって一般的に使用されており、うまく機能しますが、 function
本体の内部を調べて、どのパラメーターが必要かを確認する必要があります。 構造化されていないパラメーターを使用すると、 function
宣言でパラメーターを明確に示すことができます。
function initiateTransfer({protocol, port, delay, retries, timeout, log}) { // code to initiate transfer }; var options = { protocol: 'http', port: 800, delay: 150, retries: 10, timeout: 500, log: true } initiateTransfer(options);
この関数では、構成オブジェクトの代わりにオブジェクト破壊パターンを使用しました。 これにより、関数がより簡潔になるだけでなく、読みやすくなります。
また、非構造化パラメーターを通常のパラメーターと組み合わせることができます。
function initiateTransfer(param1, {protocol, port, delay, retries, timeout, log}) { // code to initiate transfer } initiateTransfer('some value', options);
function
呼び出しでパラメーターを省略した場合、型エラーがスローされることに注意してください。
function initiateTransfer({protocol, port, delay, retries, timeout, log}) { // code to initiate transfer } initiateTransfer(); // TypeError: Cannot match against 'undefined' or 'null'
これは、パラメーターが必要な場合の望ましい動作ですが、パラメーターをオプションにする場合はどうでしょうか。 パラメータが欠落しているときにこのエラーを防ぐには、非構造化パラメータにデフォルト値を割り当てる必要があります。
function initiateTransfer({protocol, port, delay, retries, timeout, log} = {}) { // code to initiate transfer } initiateTransfer(); // no error
この関数では、非構造化パラメーターのデフォルト値として空のオブジェクトが提供されます。 これで、この関数がパラメーターなしで呼び出されても、エラーは発生しません。
構造化されていない各パラメータにデフォルト値を割り当てることもできます。
function initiateTransfer({ protocol = 'http', port = 800, delay = 150, retries = 10, timeout = 500, log = true }) { // code to initiate transfer }
この例では、すべてのプロパティにデフォルトパラメータがあり、未定義のパラメータを手動でチェックして、 function
本体内にデフォルト値を割り当てる必要がありません。
ブラウザサポートの破壊
デスクトップブラウザ:
特徴 | クロム | Firefox | インターネットエクスプローラ | マイクロソフトエッジ | オペラ | サファリ |
---|---|---|---|---|---|---|
基本的なサポート | 49 | 2.0 | – | 14 | – | 7.1 |
デフォルト値が割り当てられた非構造化パラメーター | 49 | 47 | – | 14 | – | – |
モバイルブラウザ:
特徴 | Chrome for Android | Firefox Mobile | Safari Mobile | Opera Mobile | IEモバイル |
---|---|---|---|---|---|
基本的なサポート | 49 | 1 | 8 | – | – |
デフォルトパラメータの後にデフォルトのないパラメータ | 49 | 47 | – | – | – |
引数の受け渡し
関数に引数を渡すには、参照または値の2つの方法があります。 参照によって渡された引数の変更はグローバルに反映されますが、値によって渡された引数の変更は関数内にのみ反映されます。
Visual BasicやPowerShellなどの一部の言語では、引数を参照で渡すか値で渡すかを指定するオプションがありますが、JavaScriptの場合はそうではありません。
値による引数の受け渡し
技術的には、JavaScriptは値を渡すことしかできません。 引数を値で関数に渡すと、その値のコピーがfunction
スコープ内に作成されます。 したがって、値への変更はfunction
内にのみ反映されます。 この例を考えてみましょう。
var a = 5; function increment(a) { a = ++a; console.log(a); } increment(a); // 6 console.log(a); // 5
ここで、関数内の引数を変更しても、元の値には影響しません。 したがって、変数が関数の外部からログに記録された場合でも、出力される値は5
のままです。
参照による引数の受け渡し
JavaScriptでは、すべてが値によって渡されますが、オブジェクト(配列を含む)を参照する変数を渡す場合、「値」はオブジェクトへの参照であり、変数によって参照されるオブジェクトのプロパティを変更すると、基になるオブジェクト。
この関数について考えてみましょう。
function foo(param){ param.bar = 'new value'; } obj = { bar : 'value' } console.log(obj.bar); // value foo(obj); console.log(obj.bar); // new value
ご覧のとおり、オブジェクトのプロパティは関数内で変更されていますが、変更された値は関数外で表示されます。
配列やオブジェクトなどの非プリミティブ値を渡すと、バックグラウンドで、メモリ内の元のオブジェクトの場所を指す変数が作成されます。 次に、この変数は関数に渡され、変更すると元のオブジェクトに影響します。
タイプチェックと欠落または追加のパラメータ
強く型付けされた言語では、 function
宣言でパラメーターの型を指定する必要がありますが、JavaScriptにはこの機能がありません。 JavaScriptでは、データの種類や関数に渡す引数の数は関係ありません。
1つの引数のみを受け入れる関数があるとします。 その関数を呼び出すとき、関数に渡す引数は1つだけに制限されません。 1つ、2つ、またはそれ以上の引数を自由に渡すことができます。 何も渡さないことを選択することもでき、エラーは発生しません。
引数とパラメーターの数は、次の2つの点で異なります。
- パラメータよりも引数が少ない。
欠落しているパラメーターはundefined
に等しくなります。 - パラメータよりも多くの引数。
余分なパラメータは無視されますが、特別な配列のような変数引数を介して取得できます(次に説明します)。
必須の引数
function
呼び出しで引数が欠落している場合、引数はundefined
に設定されます。 この動作を利用して、引数を省略するとエラーをスローできます。
function foo(mandatory, optional) { if (mandatory === undefined) { throw new Error('Missing parameter: mandatory'); } }
ECMAScript 6では、これをさらに進め、デフォルトのパラメーターを使用して必須の引数を設定できます。
function throwError() { throw new Error('Missing parameter'); } function foo(param1 = throwError(), param2 = throwError()) { // do something } foo(10, 20); // ok foo(10); // Error: missing parameter
引数オブジェクト
残りのパラメーターのサポートは、 arguments
オブジェクトを置き換えることを目的としてECMAScript 4に追加されましたが、ECMAScript4は実現しませんでした。 ECMAScript 6のリリースにより、JavaScriptは残りのパラメーターを公式にサポートするようになりました。 また、 arguments
オブジェクトのサポートを終了する計画もありませんでした。
arguments
オブジェクトは、すべての関数内で使用できる配列のようなオブジェクトです。 これにより、関数に渡されたargument
の値を、名前ではなく番号で取得できます。 このオブジェクトを使用すると、関数に任意の数の引数を渡すことができます。 次のコードフラグメントについて考えてみます。
function checkParams(param1) { console.log(param1); // 2 console.log(arguments[0], arguments[1]); // 2 3 console.log(param1 + arguments[0]); // 2 + 2 } checkParams(2, 3);
この関数は、引数を1つだけ受け取ることを想定しています。 2つの引数を使用して呼び出す場合、最初の引数はパラメーター名param1
または引数オブジェクトarguments[0]
で関数にアクセスできますが、2番目の引数はarguments[1]
としてのみアクセスできます。 また、 arguments
オブジェクトは名前付き引数と組み合わせて使用できることに注意してください。
arguments
オブジェクトには、関数に渡される各引数のエントリが含まれており、最初のエントリのインデックスは0
から始まります。 上記の例でさらに多くの引数にアクセスしたい場合は、 arguments[2]
、 arguments[3]
などを記述します。
名前付きパラメーターの設定を完全にスキップして、 arguments
オブジェクトを使用することもできます。
function checkParams() { console.log(arguments[1], arguments[0], arguments[2]); } checkParams(2, 4, 6); // 4 2 6
実際、名前付きパラメーターは便利であり、必須ではありません。 同様に、残りのパラメーターを使用して、渡された引数を反映できます。
function checkParams(...params) { console.log(params[1], params[0], params[2]); // 4 2 6 console.log(arguments[1], arguments[0], arguments[2]); // 4 2 6 } checkParams(2, 4, 6);
arguments
オブジェクトは配列に似たオブジェクトですが、 slice()
やforeach()
)などの配列メソッドがありません。 arguments
オブジェクトで配列メソッドを使用するには、最初にオブジェクトを実際の配列に変換する必要があります。
function sort() { var a = Array.prototype.slice.call(arguments); return a.sort(); } sort(40, 20, 50, 30); // [20, 30, 40, 50]
この関数では、 Array.prototype.slice.call()
を、 arguments
オブジェクトを配列に変換する簡単な方法として使用します。 次に、 sort()
メソッドは配列の項目をソートして返します。
ECMAScript 6には、さらに簡単な方法があります。 ECMAScript 6の新しい追加であるArray.from()
は、配列のようなオブジェクトから新しい配列を作成します。
function sort() { var a = Array.from(arguments); return a.sort(); } sort(40, 20, 50, 30); // [20, 30, 40, 50]
長さプロパティ
引数オブジェクトは技術的には配列ではありませんが、関数に渡される引数の数を確認するために使用できるlength
プロパティがあります。
function countArguments() { console.log(arguments.length); } countArguments(); // 0 countArguments(10, null, "string"); // 3
length
プロパティを使用することで、関数に渡される引数の数をより適切に制御できます。 たとえば、関数が機能するために2つの引数が必要な場合、 length
プロパティを使用して渡された引数の数を確認し、それらが予想より少ない場合はエラーをスローできます。
function foo(param1, param2) { if (arguments.length < 2) { throw new Error("This function expects at least two arguments"); } else if (arguments.length === 2) { // do something } }
残りのパラメーターは配列であるため、 length
プロパティがあります。 ECMAScript 6では、前述のコードをRESTパラメーターを使用して書き直すことができます。
function foo(...params) { if (params.length < 2) { throw new Error("This function expects at least two arguments"); } else if (params.length === 2) { // do something } }
着信者と発信者のプロパティ
callee
プロパティは現在実行中の関数を参照し、 caller
は現在実行中の関数を呼び出した関数を参照します。 ECMAScript 5の厳密モードでは、これらのプロパティは非推奨になり、これらのプロパティにアクセスしようとするとTypeErrorが発生します。
arguments.callee
プロパティは、再帰関数(再帰関数は、その名前でそれ自体を参照する通常の関数)で、特に関数名が使用できない場合(無名関数)に役立ちます。 匿名関数には名前がないため、匿名関数を参照する唯一の方法は、 arguments.callee
を使用することです。
var result = (function(n) { if (n <= 1) { return 1; } else { return n * arguments.callee(n - 1); } })(4); // 24
厳密モードと非厳密モードの引数オブジェクト
ECMAScript 5の非厳密モードでは、 arguments
オブジェクトには通常とは異なる機能があります。それは、対応する名前付きパラメーターの値との同期を維持します。
次のコードフラグメントについて考えてみます。
function foo(param) { console.log(param === arguments[0]); // true arguments[0] = 500; console.log(param === arguments[0]); // true return param } foo(200); // 500
この関数内で、新しい値がarguments[0]
に割り当てられます。 arguments
の値は常に名前付きパラメーターの値と同期しているため、 arguments[0]
に変更すると、 param
の値も変更されます。 実際、これらは同じ変数の2つの異なる名前のようなものです。 ECMAScript 5の厳密モードでは、 arguments
オブジェクトのこの紛らわしい動作は削除されました。
"use strict"; function foo(param) { console.log(param === arguments[0]); // true arguments[0] = 500; console.log(param === arguments[0]); // false return param } foo(200); // 200
今回は、 arguments[0]
を変更してもparam
には影響せず、出力は期待どおりです。 ECMAScript 6でのこの関数の出力は、ECMAScript 5 strictモードの場合と同じですが、 function
宣言でデフォルト値が使用されている場合、 arguments
オブジェクトは影響を受けないことに注意してください。
function foo(param1, param2 = 10, param3 = 20) { console.log(param1 === arguments[0]); // true console.log(param2 === arguments[1]); // true console.log(param3 === arguments[2]); // false console.log(arguments[2]); // undefined console.log(param3); // 20 } foo('string1', 'string2');
この関数では、 param3
にデフォルト値がありますが、関数に渡される引数は2つだけであるため、 arguments[2]
と等しくありません。 つまり、デフォルト値を設定しても、 arguments
オブジェクトには影響しません。
結論
ECMAScript 6は、JavaScriptに何百もの大小の改善をもたらしました。 ますます多くの開発者がECMAScript6の機能を使用しており、まもなくこれらの機能は避けられなくなります。 このチュートリアルでは、ECMAScript 6がJavaScriptでのパラメーター処理をアップグレードする方法を学びましたが、ECMAScript 6の表面をかじったところです。この言語の他の多くの新しい、興味深い機能はチェックする価値があります。
リンク
- ECMAScript 6互換性テーブル、Juriy Zaytsev
- 「ECMAScript2015言語仕様」、ECMA International