2022年10月18日

レストパラメータとスプレッド構文

多くのJavaScript組み込み関数は、任意の数の引数をサポートしています。

例えば

  • Math.max(arg1, arg2, ..., argN) – 引数の最大値を返します。
  • Object.assign(dest, src1, ..., srcN)src1..N から dest にプロパティをコピーします。
  • …などです。

この章では、同じことを行う方法を学びます。また、そのような関数に配列を引数として渡す方法も学びます。

レストパラメータ ...

関数は、定義方法に関係なく、任意の数の引数で呼び出すことができます。

このように

function sum(a, b) {
  return a + b;
}

alert( sum(1, 2, 3, 4, 5) );

「過剰な」引数のためにエラーは発生しません。しかし、もちろん結果には最初の2つだけがカウントされるため、上記のコードの結果は3です。

残りのパラメータは、3つのドット...の後にそれらを含む配列の名前を続けることで、関数定義に含めることができます。ドットは文字通り「残りのパラメータを配列に集める」ことを意味します。

たとえば、すべての引数を配列argsに集めるには

function sumAll(...args) { // args is the name for the array
  let sum = 0;

  for (let arg of args) sum += arg;

  return sum;
}

alert( sumAll(1) ); // 1
alert( sumAll(1, 2) ); // 3
alert( sumAll(1, 2, 3) ); // 6

最初のパラメータを変数として取得し、残りのみを収集することを選択できます。

ここでは、最初の2つの引数は変数に入り、残りはtitles配列に入ります

function showName(firstName, lastName, ...titles) {
  alert( firstName + ' ' + lastName ); // Julius Caesar

  // the rest go into titles array
  // i.e. titles = ["Consul", "Imperator"]
  alert( titles[0] ); // Consul
  alert( titles[1] ); // Imperator
  alert( titles.length ); // 2
}

showName("Julius", "Caesar", "Consul", "Imperator");
レストパラメータは最後に配置する必要があります

レストパラメータは残りのすべての引数を収集するため、以下は意味がなく、エラーが発生します

function f(arg1, ...rest, arg2) { // arg2 after ...rest ?!
  // error
}

...rest は常に最後でなければなりません。

「arguments」変数

すべての引数をインデックスで含む、argumentsという名前の特別な配列のようなオブジェクトもあります。

例えば

function showName() {
  alert( arguments.length );
  alert( arguments[0] );
  alert( arguments[1] );

  // it's iterable
  // for(let arg of arguments) alert(arg);
}

// shows: 2, Julius, Caesar
showName("Julius", "Caesar");

// shows: 1, Ilya, undefined (no second argument)
showName("Ilya");

昔は、レストパラメータは言語に存在せず、argumentsを使用することが関数のすべての引数を取得する唯一の方法でした。そしてそれはまだ機能しており、古いコードで見つけることができます。

しかし、欠点は、argumentsは配列のようなものであり反復可能ですが、配列ではないということです。配列メソッドをサポートしていないため、たとえばarguments.map(...)を呼び出すことはできません。

また、常にすべての引数が含まれています。レストパラメータのように、部分的にキャプチャすることはできません。

そのため、これらの機能が必要な場合は、レストパラメータが推奨されます。

アロー関数には"arguments"がありません

アロー関数からargumentsオブジェクトにアクセスすると、外部の「通常の」関数から取得されます。

例を次に示します

function f() {
  let showArg = () => alert(arguments[0]);
  showArg();
}

f(1); // 1

覚えているように、アロー関数には独自のthisがありません。 अब हम जानते हैं कि उनके पास विशेष `arguments` वस्तु भी नहीं है।

スプレッド構文

パラメータのリストから配列を取得する方法を学びました。

しかし、時には正反対のことをする必要があります。

たとえば、リストから最大数を返す組み込み関数Math.maxがあります

alert( Math.max(3, 5, 1) ); // 5

ここで、配列[3, 5, 1]があるとします。 それでMath.maxをどのように呼び出しますか?

Math.maxは単一の配列ではなく数値引数のリストを期待しているため、「そのまま」渡しても機能しません

let arr = [3, 5, 1];

alert( Math.max(arr) ); // NaN

そして、確かにコードMath.max(arr[0], arr[1], arr[2])に手動で項目をリストすることはできません。なぜなら、いくつあるかわからないからです。 スクリプトが実行されると、たくさんある場合もあれば、まったくない場合もあります。 そしてそれは醜くなるでしょう。

スプレッド構文が救助に! レストパラメータと同様に...を使用しますが、まったく逆のことを行います。

関数呼び出しで...arrが使用されると、反復可能なオブジェクトarrを展開して引数リストにします.

Math.maxの場合

let arr = [3, 5, 1];

alert( Math.max(...arr) ); // 5 (spread turns array into a list of arguments)

このように複数の反復可能オブジェクトを渡すこともできます

let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];

alert( Math.max(...arr1, ...arr2) ); // 8

スプレッド構文を通常の値と組み合わせることさえできます

let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];

alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25

また、スプレッド構文を使用して配列をマージできます

let arr = [3, 5, 1];
let arr2 = [8, 9, 15];

let merged = [0, ...arr, 2, ...arr2];

alert(merged); // 0,3,5,1,2,8,9,15 (0, then arr, then 2, then arr2)

上記の例では、スプレッド構文を示すために配列を使用しましたが、反復可能オブジェクトであれば何でも使用できます。

たとえば、ここではスプレッド構文を使用して文字列を文字の配列に変換します

let str = "Hello";

alert( [...str] ); // H,e,l,l,o

スプレッド構文は、for..ofと同じ方法で、内部的にイテレータを使用して要素を収集します.

そのため、文字列の場合、for..ofは文字を返し、...str"H","e","l","l","o"になります。文字のリストは配列初期化子[...str]に渡されます.

この特定のタスクでは、反復可能オブジェクト(文字列など)を配列に変換するため、Array.fromを使用することもできます

let str = "Hello";

// Array.from converts an iterable into an array
alert( Array.from(str) ); // H,e,l,l,o

結果は[...str]と同じです.

しかし、Array.from(obj)[...obj]の間には微妙な違いがあります

  • Array.fromは、配列のようなものと反復可能オブジェクトの両方で動作します.
  • スプレッド構文は反復可能オブジェクトでのみ機能します.

そのため、何かを配列に変換するというタスクでは、Array.fromの方がより普遍的である傾向があります.

配列/オブジェクトのコピー

Object.assign()について以前話したのを覚えていますか?

スプレッド構文で同じことができます.

let arr = [1, 2, 3];

let arrCopy = [...arr]; // spread the array into a list of parameters
                        // then put the result into a new array

// do the arrays have the same contents?
alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true

// are the arrays equal?
alert(arr === arrCopy); // false (not same reference)

// modifying our initial array does not modify the copy:
arr.push(4);
alert(arr); // 1, 2, 3, 4
alert(arrCopy); // 1, 2, 3

オブジェクトのコピーを作成するのにも同じことができることに注意してください

let obj = { a: 1, b: 2, c: 3 };

let objCopy = { ...obj }; // spread the object into a list of parameters
                          // then return the result in a new object

// do the objects have the same contents?
alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true

// are the objects equal?
alert(obj === objCopy); // false (not same reference)

// modifying our initial object does not modify the copy:
obj.d = 4;
alert(JSON.stringify(obj)); // {"a":1,"b":2,"c":3,"d":4}
alert(JSON.stringify(objCopy)); // {"a":1,"b":2,"c":3}

このオブジェクトのコピー方法は、let objCopy = Object.assign({}, obj)または配列の場合はlet arrCopy = Object.assign([], arr)よりもはるかに短いため、可能な限りこれを使用することをお勧めします.

まとめ

コードで"..."が表示された場合、それはレストパラメータかスプレッド構文のいずれかです.

それらを区別する簡単な方法があります

  • ...が関数パラメータの最後にある場合、それは「レストパラメータ」であり、引数リストの残りを配列にまとめます.
  • ...が関数呼び出しなどで発生した場合、それは「スプレッド構文」と呼ばれ、配列をリストに展開します.

使用パターン

  • レストパラメータは、任意の数の引数を受け入れる関数を作成するために使用されます.
  • スプレッド構文は、通常多くの引数のリストを必要とする関数に配列を渡すために使用されます.

これらを組み合わせることで、リストとパラメータの配列を簡単に変換できます.

関数呼び出しのすべての引数は、「旧式の」arguments:配列のような反復可能オブジェクトでも使用できます.

チュートリアルマップ

コメント

コメントする前にこれを読んでください…
  • 改善すべき点がある場合は、コメントする代わりにGitHubのissueまたはプルリクエストを送信してください.
  • 記事の内容が理解できない場合は、詳しく説明してください.
  • 数語のコードを挿入するには、<code>タグを使用し、複数行の場合は<pre>タグで囲み、10行を超える場合はサンドボックス(plnkrjsbincodepen…)を使用してください.