🐶

JavaScriptの...ってなに?(残余引数とスプレッド構文について)

2023/11/09に公開

1.記事の目的

今回はJSでたまに見かける三点(...)リーダーについて取り上げたいと思います。

JSを書いたり読んだりしていると、こんなものを見かけることがあります。

function sum(...numbers) {
  let result = 0;
  for (const number of numbers) {
    result += number;
  }
  return result;
}

console.log(sum(1,2,3,4,5)); // 15

console.log(sum(6,7,8)); // 21
function sum(a, b, c) {
  return a + b + c;
}

const numbers = [1, 2, 3];

console.log(sum(...numbers)); // 6

この2つのコードの三点リーダーは名前も役割も違う別のものです。
一つ目が残余引数、二つ目がスプレット構文というものです。

2.残余引数

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/rest_parameters

残余引数構文により、関数が不定数の引数を配列として受け入れることができ、可変長引数関数を JavaScript で表すことができます。

不定数の引数を配列として受け入れられます。

function sum(...numbers) { // 与えられた引数1,2,3,4,5を配列[1,2,3,4,5]として受け入れ
// numbers = [1,2,3,4,5]; という状態になっています
  let result = 0;
  for (const number of numbers) { // 配列に使用できるfor-of文が使用できる
    result += number;
  }
  return result;
}

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

残余引数は配列なので配列のメソッドが使用できます。

function length(...numbers) {
  console.log(numbers.length); // lengthメソッドが使用できる
}

length(1,2,3,4,5); // 5
length(1,2,3); // 3

また、残余引数なので、不定数の引数のうち特定の仮引数に代入されない残りとして設定することができます。

function log(...numbers) { // もちろんこれはOK
  console.log(numbers); 
} 

log(1,2,3); // [1,2,3]
function log(a,...numbers) { // これもOK
    console.log(a);
  console.log(numbers); 
} 

log(1,2,3); // 1 [2,3]; →aに1が代入され、numbersに残りが代入される
function log(...numbers,b) { // これはダメ
  console.log(numbers); 
  console.log(b);
} 

log(1,2,3);  // Uncaught SyntaxError: Rest parameter must be last formal parameter というエラーが発生する

あと、残余引数を2つ設定するようなこともできません。

function log(...numbersA,...numbersB) { // これもダメ
 // 略
}
  • まとめ
    残余引数は不定数の引数丸ごと(または特定の引数にいくつか代入した残り)を、まとめて配列として代入できます。

3.スプレッド構文

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax

配列式や文字列などの反復可能オブジェクトを、0 個以上の引数 (関数呼び出しの場合) や要素 (配列リテラルの場合) を期待された場所で展開したり、オブジェクト式を、0 個以上のキーと値の組 (オブジェクトリテラルの場合) を期待された場所で展開したりすることができます。

配列や文字列など反復可能オブジェクトに対して使える構文です。

function sum(a, b, c) { // 引数に3つの仮引数が設定されている
  return a + b + c;
}

const numbers = [1, 2, 3]; // この配列を仮引数a,b,cに与えたい

sum(...numbers); // このように書くとa=1,b=2,c=3と展開してくれる

配列を展開して引数一つ一つに格納するという面倒な動きを一気に行ってくれます。
スプレッド(spread:広げる)な構文なんですね。

引数に展開するだけではなく、例えば配列を組み合わせて新たな配列を作るようなこともできます。

let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];

arr1 = [...arr1, ...arr2]; 
console.log(arr1); // [0, 1, 2, 3, 4, 5]

また、オブジェクトもスプレッド構文の対象になっています。

let obj1 = { a: "あ", b: 1 };

let clonedObj = { ...obj1 };

console.log(clonedObj); // {a: 'あ', b: 1}
  • まとめ
    スプレッド構文は配列など反復可能なオブジェクトを引数や要素として展開できます。

4.まとめ

最初は全く残余引数とスプレッド構文の違いがついていませんでしたが、似ているけど全く別もの(残余引数はまとめる、スプレッド構文は広げる)なんだ、と気づいた時にそれまでの混乱が解消されたのを覚えています。

技術的に明らかな誤りに気づいた方は、ご指摘いただけると嬉しいです。

5.参考記事

https://qiita.com/azu_nyan/items/6735c1cf08e8f650bda7

https://zenn.dev/y__adler/articles/940d36b1be89a8

Discussion