はじめに
JavaScriptのbind()
は簡単に言えば、あらかじめ作成した関数のパラメーター(this)を変更して新しい関数を作れるメソッドです。
英単語bindの意味をそのまま辿ると「拘束」や「結びつけ」といった意味で、何ができるのか想像しにくいですね。
少し強引に言葉を補って...
すでにある他の関数を拘束して
他のオブジェクトのパラメータ(this)や引数と結びつけ
新しい関数を生成する
と読み替えると、bind()の動きが少しイメージしやすくなるかもしれません。
構文に当てはめると以下のようになり少し簡単そうに見えてきませんか?
// すでにある他の関数.bind(パラメータthis, 引数1, 引数2, ... ,引数N)
func.bind(thisArg, arg1, arg2, ... ,argN)
以下より
飛行機の「空を飛ぶ」という機能を拘束し
パラメータを車に結びつけて車が空を飛ぶ
といったサンプル作りながら、bind()の使い方を紹介していきます。
bind()の使い方
bind()を使うための予備知識として、知っておくことは以下の2つです。
1. thisの参照先
2. 引数の使い方
1. thisの参照先
thisとは「記述場所によって参照先の変わる特別な変数」です。
thisの参照先は、function(関数)をどのオブジェクトのメソッドとして呼び出すか
で変わります。
var fn = function() { // グローバルに関数 fn を定義
console.log(this);
};
fn(); // -> thisの参照先は window (グローバル)
グルーバルに定義した関数内のthisの参照先をconsole.log()で確認してみると、ブラウザ上でのグローバルを意味する「window」を参照していることがわかります。
これは「グローバルに定義した関数 = windowオブジェクトのメソッド
」であることを示しています。
次にオブジェクトを作成し、その中に定義したメソッド(関数)のthisの参照先を確認します。
var obj = { // グローバルにオブジェクト obj を定義
fn: function() { // メソッド fn を定義
console.log(this);
}
};
obj.fn(); // -> 関数内のthisの参照先は obj (そのオブジェクト自身)
この例では「obj」のメソッドとしてfnを定義
しているので、fnを呼び出すときにthisの参照先になるオブジェクトは「obj」です。
2. 引数の使い方
構文の確認
// 対象の関数名.bind(thisの参照先, 引数1, 引数2, ... ,引数N)
func.bind(thisArg, arg1, arg2, ... ,argN)
対象の関数名.bind(thisの参照先, 引数1, 引数2,...引数N)
と記述すれば、対象の関数のthisの参照先を変更した関数を新たに生成することが出来ます。
ここでの「引数1, 引数2,...引数N」は、対象の関数で使用する引数になります。
引数 | 指定内容 |
---|---|
第1引数 | thisの参照先を指定 |
第2引数 以降 | 対象の関数で使用する引数を指定 |
第1引数を指定する
bind()を使えば、第1引数の指定で対象の関数の「thisの参照先を変える」ことができます。例えば、オブジェクト内に定義した関数内のthisの参照先を「obj」 → 「window」に変える。と言ったようなことができます。
var obj = { // グローバルにオブジェクト obj を定義
fn: function() { // メソッド「fn」を定義
console.log(this);
}
};
// thisの参照先を確認
obj.fn(); // -> thisの参照先は obj
var bindFnc = obj.fn.bind(window); // 第1引数に window を指定 -> thisの参照先は window に変わる
bindFnc(null); // 第1引数に null を指定 -> thisの参照先は window に変わる
bindFnc('undefind'); // 第1引数に 'undefind' を指定 -> thisの参照先は window に変わる
第1引数にnull
や'undefind'
を指定した場合、thisの参照先は「window」になります。
以下から車が空を飛ぶサンプルを作っていきます。
// 飛行機オブジェクト
var airplane = {
name: '飛行機',
fly: function(){ // bind()の対象となる関数メソッド
console.log(this.name + 'が空を飛びます!');
}
};
airplane.fly(); // '飛行機が空を飛びます!
// carオブジェクト
var car = {
name: '車',
};
// bind() の場合(第1引数に carオブジェクト を指定)
var flyCar = airplane.fly.bind(car); // this.name は carオブジェクトの name'車' を参照
flyCar(); // 車が空を飛びます!
// call() の場合(比較用)
airplane.fly.call(car); // 車が空を飛びます!
動きが似ているcall()
の場合と比較してみると
-
call()は呼び出した関数をthisの参照先を変更してそのまま実行できる
(ここでは airplane.fly をそのまま実行) -
bind()は関数の実行ではなく...
戻り値として新しい関数を生成し返す
ため
無名関数を宣言するように変数 = 対象の関数.bind()
で生成した関数を変数に格納してから実行する
といった違いがあります。
// 無理やり即実行
airplane.fly.bind(car)();
// typeofで型判定(関数であれば function)
console.log(typeof airplane.fly.bind(car)); // function
var flyCar = airplane.fly.bind(car);
console.log(typeof flyCar); // function
関数を生成しているので、上記のような書き方で関数生成後そのまま実行できたりします。(こんな使い方はもちろんしませんが)
第2引数以降を指定する
// 飛行機オブジェクト
var airplane = {
name: '飛行機',
fly: function(destination, speed){ // bind()の対象となる関数メソッド
console.log(this.name + 'は' + destination + 'へ向かい' + speed + '飛び立ちました!');
}
};
// 車オブジェクト
var car = {
name: '車',
};
// bind() の場合
var flyCar = airplane.fly.bind(car, '2015年10月21日', '慌ただしく');
flyCar(); // 車は2015年10月21日へ向かい慌ただしく飛び立ちました!
// call() の場合(比較用)
airplane.fly.call(car, '2015年10月21日', '慌ただしく'); // 車は2015年10月21日へ向かい慌ただしく飛び立ちました!
予備知識の説明に沿えばこんな感じに書けますが、
// 飛行機:コンストラクタ(関数オブジェクト)
var Airplane = function () {
this.name = '飛行機';
this.fly = function(destination, speed){ // bind()の対象となる関数メソッド
console.log(this.name + 'は' + destination + 'へ向かい' + speed + '飛び立ちました!');
};
};
// 車:コンストラクタ(関数オブジェクト)
var Car = function () {
this.name = '車';
};
// bind() の場合
var flyCar = new Airplane().fly.bind(new Car(), '2015年10月21日', '慌ただしく');
flyCar(); // 車は2015年10月21日へ向かい慌ただしく飛び立ちました!
// call() の場合(比較用)
new Airplane().fly.call(new Car(), '2015年10月21日', '慌ただしく'); // 車は2015年10月21日へ向かい慌ただしく飛び立ちました!
と言ったように、コンストラクタを利用することもできます。
bind()の部分は以下のように、第2引数以降を実行するときに書くこともできます。
var flyCar = airplane.fly.bind(car);
flyCar('2015年10月21日', '慌ただしく');
var flyCar = new Airplane().fly.bind(new Car());
flyCar('2015年10月21日', '慌ただしく');
おわりに
以前、紹介したcall()とは違った紹介ができればと思いましたが、動作が似ていることもありcall()の記事をなぞる形になりました。
※ 今後はbind()を使ったサンプルを追加する予定です。