Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

今回は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の値渡し/参照渡しについてまとめます。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away