Javascriptのbind関数と部分適用 〜 JSおくのほそ道 #015

  • 167
    いいね
  • 1
    コメント
この記事は最終更新日から1年以上が経過しています。

こんにちは、ほそ道です。

今回はFunction.prototype.bind関数を取り上げます。
このbind関数も前回のcall/apply関数同様、開発者の意図が反映される関数かと思います。

目次はこちら

bind関数の仕様

bind関数はFunction.prototypeに属し、新たな関数を生成して返します。
下記で仕様を見ていきます。

その1:thisを強制変更する

第一引数は関数内で参照されるthisを置換えます。

thisを強制変更する
// 人間
function Man(name) {
  this.name = name;
  this.greet = function() {
    console.log("Hello, my name is " + this.name);
  };
}

// ネコ
function Cat(name) {
  this.name = name;
}

// 人間の挨拶
var steve = new Man("Steve");
steve.greet();           // Hello, my name is Steve

// ネコの挨拶!?
var tama = new Cat("Tama");
var tamaGreet = steve.greet.bind(tama);
tamaGreet();            // Hello, my name is Tama

bind関数を使う事で本来ネコが持っていないgreet関数が使えます。怖可愛いですねぇ。。

ん、コレって既視感を覚える、と思った方、Yesです!
そう、前回やったcall関数にクリソツなんですね!
大きな違いとしては
call関数は関数を実行するのに対し、
bind関数は新たな関数を生成して返すんですね。

その2:引数を予約する

bind関数は第二引数以降も取る事が出来ます。
第二引数以降は引数の予約に使われます。

引数を予約する
var neoMax = Math.max.bind(null, 200, 300, 400);
console.log(neoMax(100));    // 400
console.log(neoMax(800));    // 800

上記のようにMath.max関数を流用して、あらかじめ第1〜3引数を特定値200,300,400で占有させたneoMax関数を生成出来ます。
call/apply関数の時は 「汎化」 という言葉を使いましたが、
このように引数を予約して生成された関数は 「特化」 されると言って良いのではないでしょうか?

部分適用

特化関数の生成をちょっと掘り下げましょう。
bind関数の引数予約を使うと部分適用が簡潔にできます。
ビフォーアフター的にbind関数を使うケース、使わないケースを見ていきましょう。

bindをつかわない部分適用

bindをつかわない部分適用
function add(x, y) {
  if (typeof y === 'undefined') {
    return function(y) {
      return x + y;
    };
  }
}

// 関数を部分適用
var add200 = add(200);
console.log(add200(300));    // 500

第二引数であるyが無ければ新たな部分適用された関数を返します。
次はbindをつかったパターンも見ていきます。

bindをつかった部分適用

bindをつかった部分適用
function add(x, y) {
  return x + y;
}

// 関数を部分適用
var add200 = add.bind(null, 200);
console.log(add200(300));     // 500

と、簡潔に書くことができます。

ただし、気をつけるポイントとして
bind関数を使う場合はadd関数内部では部分適用が意識されていません。
別の場所で実行された処理がadd関数を流用して特化関数を作成しているという事がひとつの特徴になっており、
どれが正解という訳ではないですが、プログラムの文脈に即した使い方が出来るように配慮しておく事が大事かなと思います。

カリー化について

もともとのポストで部分適用とカリー化について誤解があった状態で内容記述していた為、改めて整理しておきます。

カリー化 (currying, カリー化された=curried) とは、複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること(あるいはその関数のこと)である。from wikipedia

下記のような感じですね。 ご指摘いただいた皆様には心より感謝申し上げます。。:pray:

function add(x) { return function(y) { return x + y; } }
console.log(add(150)(350));  // 500

今回は以上です。
次回はJavascriptの値渡し/参照渡しについてまとめます。