Javascriptでオーバーロード

ES2015以前

javascriptにはオーバーロード機能が本来ありませんが、argumentsで引数を呼び出すことができる場合には次のようにすることで擬似的なオーバーロードが実現できるとされていました。

function overloading(arg1, arg2) {
  switch (arguments.length) {
    case 2:
      return funcFor2arguments(arg1, arg2);
    case 1:
      return funcFor1argument(arg1);
    default:
      throw "inappropriate argument number!";
  }
}
function funcFor2arguments(arg1, arg2) { /* 2引数の場合の処理 */ }
function funcFor1arguments(arg1) { /* 1引数の場合の処理 */ }

可変長引数の利用

ところで、ES2015では可変長引数をとる関数が定義できますので、argumentsという黒魔術を利用しなくても以下のようにしてオーバーロードもどきが実現可能です。 argumentsは例えばアロー関数の中などでは利用できないので、こちらのほうがより安全です。

function overloading(...args) {
  switch (args.length) {
    case 2:
      return funcFor2arguments(arg[0], arg[1]);
    case 1:
      return funcFor1argument(arg[0]);
    default:
      throw "inappropriate argument number!";
  }
}
function funcFor2arguments(arg1, arg2) { /* 2引数の場合の処理 */ }
function funcFor1arguments(arg1) { /* 1引数の場合の処理 */ }

この方法を用いて先日のコードを修正すると、次のようになります。

function call(main, ...args) {
  const obj = originalCall(main, ...args);
  obj.withTimeout = (...args) => {
    const [time, after] = args; // 配列argsの0番要素と1番要素をそれぞれtime, afterにバインド
    if (!after) { // 引数が1つしか渡されていない場合、1番要素に対応するafterはundefinedになる
      return race({
        result: obj,
        isTimeout: call(delay, time)
      });
    } else {
      return race({
        result: obj,
        isTimeout: delay(time).then(after)
      });
    }
  };
  return obj;
}

わりと読みやすくなった気がします。

おまけ

ここまで書いたところでjavascriptの柔軟性に思いを致し、「もしや・・・」と以下のように書いてみたらこれでも通りました。

function call(main, ...args) {
  const obj = originalCall(main, ...args);
  obj.withTimeout = (time, after) => {
    if (!after) {
      return race({
        result: obj,
        isTimeout: call(delay, time)
      });
    } else {
      return race({
        result: obj,
        isTimeout: delay(time).then(after)
      });
    }
  };
  return obj;
}

適切な書き方なのかはよくわかりませんが、単純なオーバーロードならこれでも十分っぽそうです。 (強いて言えば、第二引数にundefinedやfalseが渡されると二引数にもかかわらず一引数の場合の処理が呼び出されることになりそうですが、むしろ望ましい動作のような気もします)

参考

JavaScriptの使える小技です。: Javascriptでオーバーロードを実現する