初心者がつまづくメソッドのひとつにbind()メソッドがあります。おそらく中級者以上でもなぁなぁになって正面から向き合わずに過ごしてしまっている方も多い気がします。フレームワークのReactでもbind()メソッドはよく使いますね。
どのサイトを見てもbind()メソッドの解説が難解になる傾向がある(特にMozillaの公式)ので、この機会に初学者にも説明できるように整理しておきます。
bind()ダルいんだけど
分かる、分るよぉ
そもそもbindとはどういった意味か?
MDNの解説ページにはbind()メソッドの説明の最初に次のような文章を掲載しています。
bind() メソッドは、呼び出された際に this キーワードに指定された値が設定される新しい関数を生成します。この値は新しい関数が呼び出されたとき、一連の引数の前に置かれます。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
分かり難いにもほどがあります。こんな説明をされても、よっぽどJavaScript歴が長く、ある程度理解しきっている人にしか通じません。おそらく初心者の8割がこの時点で離脱します。
MDNの日本語サイトは単に英語の本家を意訳しているだけなのでしょうね。引用を掲載しましたが、ちゃんと読んだ人はいないと踏んでいます。
話が飛びましたが、プログラムは英語で成立しているので、各メソッドの本質を理解しようとする際は、その英単語の意味を調べることが大切です。
slice()メソッドやsplit()メソッドなどは、英単語の意味を推測することは容易かと思います。ですがbind()や、例えばtoFixed()メソッドなどは、パッと見て判断が少し難しい部類に入るのではないでしょうか。
そこで活躍するのが英和辞書です。理解しにくいメソッドが出てきた場合には辞書を引いたり、Googleで和訳を調べることが結構有効です。
では、bindにはどういった意味があるのでしょうか?英辞郎によると「縛る」「巻く」「くっつけて固める」などが挙げられています。
JavaScriptにおけるbind()メソッドでは「縛る」や「束縛」「拘束」という意味が最も適切な日本語です。
bind()メソッドは何を「縛る」のか
縛るといった日本語に対応する英語のbindですが、一体何を縛るのでしょうか……
実は、bind()メソッドが拘束するものは2つあります。
とりあえず、この2つに縛りをかける、とだけ意識しておけばOKです。
オブジェクトについての補足は別記事にまとめてありますのでそちらを参照してみてください。
オブジェクト内の「this」を束縛(bind)するとは?
1つ目の「オブジェクト内のthisを束縛(縛る)」とはどういうことでしょうか?
bind()メソッドの実例として最もメジャーなsetTimeout()を利用した例を掲載します。コードを丁寧に読んでみてください。難しいことはなく、初心者レベルのコードです。
// (1.1) gameオブジェクトを生成
//-----------------------------------------
let game = {
title: "ELDEN RING",
chomechome: "Fxxk Off",
comment() {
console.log(`${this.title} is too difficult!! ${this.chomechome}!!`);
}
};
// (1.2) 0.4秒後にgame.comment()メソッドを実行
//-----------------------------------------
setTimeout(function() {
game.comment(); // 注)commentの後ろに()を付けているのはfunction(){}ブロック内にあるためです。()が無いと動作しません。
}, 400);
// (1.3) 実行結果(コンソール出力)
//-----------------------------------------
ELDEN RING is too difficult!! Fxxk Off!!
上記の例ではsetTimeout()メソッドを用いてブラウザで読み込まれた0.4秒後にコンソールのログに文字列を出力するものです。ちょっと汚い言葉ですが、きちんとログに出力されます。
function(){}ブロック内であればthis.title
、this.chomechome
を正常にオブジェクト内から引っ張ってくることが出来るのが分かります。
このSetTimeout()メソッドは引数に「function(){…}」を記述しなくて良い方法があります。上記の例を少し修正してSetTimeout()のシンプルな書き方にしてみます。
次の(2.2)に注目してみてください。
// (2.1) gameオブジェクトを生成
//////////////////////////////////////
let game = {
title: "ELDEN RING",
chomechome: "Fxxk Off",
comment() {
console.log(`${this.title} is too difficult!! ${this.chomechome}!!`);
}
};
// (2.2) 0.4秒後にgame.comment()メソッドを実行
//////////////////////////////////////
setTimeout(game.comment, 400); // 注)この場合は、()は無くても可です。
// (2.3) 実行結果(コンソール出力)
//////////////////////////////////////
undefined is too difficult!! undefined!! // 注)titleとchomechomeが取得出来ていないことに注してください
サンプルコードの(1.2)と(2.2)を比較するとだいぶスッキリコードが書けているように見えます。
しかしサンプルコードの(2.2)は、期待した動作にならず、「undefined is too difficult!! undefined!!」とコンソールに表示されてしまいます。(実際にコピペしてChromeのオーバーライド機能でも使って確認してみてください。)
この不具合はgameオブジェクトの「this」がうまく参照出来ていないことが原因です。
これを解消するために、gameオブジェクトの「this」をgameオブジェクトから逸脱しないように拘束してあげる必要があるんですね。それが「bind(縛る、拘束、束縛)」です。
bind()メソッドのスニペット
前提として、bind()メソッドの記述方法を頭に入れておきましょう。bind()メソッドはオブジェクトで生成したメソッドの後ろに呼び出し、引数をオブジェクト自身にします。ちょっとわかりにくいですね。実際に書いてみましょう。
obj.anyMethod.bind(obj); // bindの引数にobjを入れることに気を付けてください。ややこしいのでとりあえず「入れる!」と覚えてしまいましょう。
bind()メソッドを分かりにくくしている要因のひとつに、引数にオブジェクト自身を入れるという使い方のため、頭でイメージがしにくくなってしまったり、こんがらがってしまうためだと思っています。
初心者や慣れていない方はここは「何でそうなるのか?」は一旦頭の隅に置いておいて、余裕が出来たら調べてみてください。JavaScriptのコアな部分に触れる必要があるのでここでは割愛します。すみませんお力になれず……図解したらもしかしたら簡単に説明できるかもですが、ちょっと今の段階ではヒラメキが足りずまたの機会に解説したいと思います。
話を元に戻すと、先ほどの2番目のサンプルをbind()メソッドを使って置き換えてみます。
// (3.1) gameオブジェクトを生成
//////////////////////////////////////
let game = {
title: "ELDEN RING",
chomechome: "Fxxk Off",
comment() {
console.log(`${this.title} is too difficult!! ${this.chomechome}!!`);
}
};
// (3.2) 0.4秒後にgame.comment()メソッドを実行
//////////////////////////////////////
setTimeout(game.comment.bind(game), 400);
// (3.3) 実行結果(コンソール出力)
//////////////////////////////////////
ELDEN RING is too difficult!! Fxxk Off!!
なんと「game.comment.bind(game)」と修正すると、1つ目の例と同様に期待する文字列が出力されました。「ELDEN RING is too difficult!! Fxxk Off!!」。
この「game.comment.bind(game)
」を誤解を恐れず勇気をもって我ら日本人のために和訳すると「gameオブジェクトのcommentメソッドを実行してね、ただしgameオブジェクトのthisは拘束して外に出ないようにね」となります。
和訳するとなんとなく感覚がつかめてきませんでしょうか……bind()メソッドのの引数にオブジェクト自身が入るのは「bind(game)
= gameオブジェクトのthisは拘束して外に出ないように」という命令なんですね。
関数の引数を縛るとはどういうことか?
bind()メソッドは宣言した関数の引数に縛りを加えることも出来ます。おそらく何のことか分からないので実例を見ながら解説していきます。
// (4.1) 2つの引数を加算する関数 myFunction()
//////////////////////////////////////
let myFunction = function(a, b) {
console.log(a + b);
}
// (4.2) 単純な呼び出し
//////////////////////////////////////
myFunction(2, 3); // コンソールログ:5
// (4.3) bind()メソッドを利用してmyFunction()の引数に縛りを与える(=第一引数を固定する)
//////////////////////////////////////
var myFunc02 = myfunction.bind(null, 4); // bindの第一引数にnullを指定するのはお約束・thisを所有しているオブジェクト名でも可能
// (4.4) 引数が固定された状態(4.3)で関数を実行する
//////////////////////////////////////
myFunc02(7); // コンソールログ:11
肝になるのはmyfunction.bind(null, 4);
の部分ですね。bind()メソッドの第一引数に「null」、第二引数に「4」をしていしています。
「null」の部分には「this」を所有するオブジェクトを入れても動作しますが、上記サンプルではthisが必要ないため、「null」を入れています。bind()メソッドのお約束で、引数を固定する用途で使う時は、bind(オブジェクトorNull, (初期値)第一引数固定……)のように記載します。
初心者ではそうそう使う機会がないのであまり深入りしませんが、Reactなどのフロントエンド開発では、カウントアップ処理やカウントダウン処理に.bind(null, なんちゃら)
といった具合で使うことが多いです。利用シーンがあったり、既存のプロジェクトに記載されていたりした場合に対応出来るように頭に置いておきましょう。
まとめ
bind()メソッドの英語の意味を「縛る」「束縛」と解釈することで、だいぶ頭の中がスッキリしていただけたら嬉しく思います。
bind()が束縛するのは、1.オブジェクトのthis、2.関数の引数、の2点です。分からないことはノートでも自身のブログでも構いませんので文章にして整理することをおすすめします。
では、みなさんがbind()を克服できることを願って!