現代的JavaScriptおれおれアドベントカレンダー2017 – 17日目

概要

...foo のようにして、「残り」を配列に突っ込めます。

function foo(a, b, ...rest) {}

あと配列の分割代入でも使えます。

const [a, b, ...rest] = arr;

加えて、まだ未確定の仕様ではオブジェクトの方でも使えるようになるっぽい。

const { a, b, ...rest } = obj;

使い方

分割代入でも関数の仮引数で使えます。

const arr = [1, 2, 3, 4, 5];

const [a, b, ...rest] = arr;
console.log(a, b, rest);  // => 1, 2, [3, 4, 5]
function foo(a, b, ...rest) {
  console.log(a, b, rest);  // => 1, 2, [3, 4, 5]
}

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

全部を受け取る

他を書かなければまるごと全部もらえます。

arr.concat() の代わりに浅いクローンで使えるかも。

const arr = [1, 2, 3, 4, 5];

const [...arr2] = arr;
console.log(arr);  // => [ 1, 2, 3, 4, 5 ]
console.log(arr2);  // => [ 1, 2, 3, 4, 5 ]
console.log(arr === arr2);  // => false

... で arguments を置き換える

arguments は引数が格納された配列風オブジェクトです。関数内で this みたいなノリでいきなり使えます。

function arrrrrg() {
  console.log(arguments.length, arguments[0]);
}

arrrrrg();  // 0, undefined
arrrrrg(100, 200, 300);  // 3, 100

で、これを ... で置き換えることができます。

function arrrrrg(...args) {
  console.log(args.length, args[0]);
}

arrrrrg();  // 0, undefined
arrrrrg(100, 200, 300);  // 3, 100

arguments には他にも( ... では置き換えられない)機能があるので、チラ見しとくと何かの役に立つかもです。

fn.apply()

他の関数を我が物顔で実行するのに使える Function#apply() というのがありまして。

arguments と組み合わせて使うと、他の関数をそのまま呼びなおすことができました。これも当然残余引数 ... で置き換えることができます。

const obj = {
  xxx: 'Hehehe',

  main(a, b, c) {
    console.log(this.xxx, a, `c is ${c}`);
  },
};

// 他の関数を呼ぶだけ
function justCallMain(...args) {
  obj.main.apply({ xxx: 'Yo!', }, args);
}

obj.main(11, 22, 33);  // "Hehehe", 11, "c is 33"
justCallMain(11, 22, 33);  // "Yo!", 11, "c is 33"

大昔にそんな話もしましたね。

引数の先頭に応じて別の処理を呼ぶ

(伝われ)

なんかこういうのあるよね。デザインパターンで名前付いてるのかな。

const robot = {
  command(type, ...args) {
    // typeに応じてメソッドを呼ぶ
    switch (type) {
      case 'walk':
        this.move(4, ...args);
        break;

      case 'run':
        this.move(16, ...args);
        break;

      case 'move':
        this.move(...args);
        break;

      case 'sing':
        this.sing(...args);
        break;

      // ...
    }
  },

  // ...
};

// 北へ向かって歩く
robot.command('walk', 'north');

// 東へ向かって走る
robot.command('run', 'east');

// 北へ8 km/hで移動
robot.command('move', 8, 'north');

// 突然歌いだす
robot.command('sing', 'あわてんぼうのサンタクロース');

仮引数じゃなくて関数呼び出しの引数で ... 使ってるのはスプレッド演算子です。別稿参照。

あと配列の分割代入でも同じような組み合わせやりましたねー。

オブジェクトの残余代入

ES2017までにはまだ存在していない未来仕様です。ES2018に入りそう。

const obj = { a: 1, b: 2, c: 3, d: 4 };

const { a, b, ...rest } = obj;
console.log(a, b, rest);  // => 1, 2, { c: 3, d: 4 }

執筆時点でChrome、Firefoxとあと手元のNode.js v8.5で動いてます。

一方babel-preset-envのv1.6.1では対応してないっぽい。

SyntaxError: /path/to/project/main.js: Unexpected token (2:14)
  1 | const obj = { a: 1, b: 2, c: 3 };
> 2 | const { a, b, ...rest } = obj;
    |               ^

(細かいバージョンは調べてません。)

その他

名前

引数の方、MDNだと “rest parameters” になってて、日本語訳で「残余引数」なんて呼んでるひとが多いみたいです。

変数の方はどう呼ぶんだろ。とりあえず「残余代入」なんて書いてはみたけれど。再びMDN(の英語版)を見ると “rest in arrays” 、 “rest in objects” という名前で “Browser compatibility” の表に記載されています。

可変長引数

他の言語だとこう呼ぶ場合が多いと思う。(引数の場合だけだけど。)

英語は “variadic” と呼ぶっぽい? (Wikipedia英語版を見た。)

参考

更新履歴

  • 2017-12-17 fn.apply() の話を追加
  • 2017-12-17 arguments の話を追加