Note

3年後の自分のために書いています

JavaScript のスプレッド構文とレスト構文がややこしいので Ruby の配列展開と splat パラメータで理解する

細かい仕様の違いはあるかもしれないが、大体以下の感じで理解している。

スプレッド構文

Array に関しては Ruby でいう配列の展開(*)とほぼ同じかな。

スプレッド構文を使うと、関数呼び出しでは 0 個以上の引数として、Array リテラルでは 0 個以上の要素として、Object リテラルでは 0 個以上の key-value のペアとして、Array や String などの iterable オブジェクトをその場で展開します。

function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];

console.log(sum(...numbers));
// expected output: 6

console.log(sum.apply(null, numbers));
// expected output: 6

関数呼び出しとかと一緒に使わないで単独で ...numbers とかってやると syntax error になる。(これは Ruby も同じ)

Object に関してはまだ proposal っぽいけど、JS の方はそのまま Object のまま展開されるのに対して、 Ruby のハッシュ展開は以下のように配列に変換されるのでちょっと仕様が違いそう。

$ irb

> hash = { x: 1, y: 2, z: 3 }
=> {:x=>1, :y=>2, :z=>3}
> a, b, c = *hash
=> [[:x, 1], [:y, 2], [:z, 3]]
> a
=> [:x, 1]
> b
=> [:y, 2]
> c
=> [:z, 3]

レスト構文(分割代入)

レスト構文はスプレッド構文と全く同じ見た目をしていますが、Array や Object の分割代入に使われます。こちらはスプレッド構文とは逆の働きといえます: スプレッド構文が要素を展開するのに対して、レスト構文は複数の要素を集約して 1 つのオブジェクトにします。

Array

Ruby で可変長引数を扱うための splat パラメータ(*)とほぼ同じかな。

function sum(...theArgs) {
  return theArgs.reduce((previous, current) => {
    return previous + current;
  });
}

console.log(sum(1, 2, 3));
// expected output: 6

console.log(sum(1, 2, 3, 4));
// expected output: 10

Object

Ruby のダブル splat パラメータ(**)の object でやり取りする版かな。(Ruby のはキーワード引数リストをハッシュとして受け取る)

今はまだ Proposal のようだ。

Rest/Spread Properties for ECMAScript 提案 (ステージ 3) は、分割代入に rest 構文を追加します。残余プロパティは、分割パターンによってすでに取り出されていない、残りの列挙可能なプロパティのキーを収集します。

let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
a; // 10 
b; // 20 
rest; // { c: 30, d: 40 }

参考

スプレッド構文 - JavaScript | MDN

Rest parameters - JavaScript | MDN

分割代入 - JavaScript | MDN

GitHub - tc39/proposal-object-rest-spread: Rest/Spread Properties for ECMAScript

Rubyのパラメータと引数の対応付けを理解する(前編)

クラス/メソッドの定義 (Ruby 2.6.0)

メソッド呼び出し(super・ブロック付き・yield) (Ruby 2.6.0)