【初心者向け】JavaScriptのbindって何??を理解する

この文書を読んでいるということはJavaScriptの初心者の方でbindメソッドがどのようなものか理解できていないと思うので、まずMDN Web Docsに記述されているbindの説明を確認します。
bindメソッドは、呼び出された際にthisキーワードに指定された値が設定される新しい関数を生成します。
bindメソッドが理解できていない時は上記の説明を読んでもわからないと思います。今意味がわからなくてもbindはthisと関係していること、bindを利用することである関数から新しい関数を作成することができることの2つを頭に入れて下記の文書を読み進めていけば最終的には上記の意味が掴めるかと思います。
目次
thisの中身を確認する
例を使いながらbindの利用方法を確認していきますが、bindはthisと関係があるのでthisに注目しながら動作確認を行っていきます。
プロパティcontentとclickを持つmyButtonを定義し、clickメソッドを実行してthisにはどのようなものが入っているのか確認を行います。
const myButton = {
content: 'OK',
click() {
console.log(this)
console.log(this.content + ' clicked');
}
};
myButton.click();
実行するとthisにはmyButtonのオブジェクトが入っていることが確認できます。ここではメソッド内でthisを使って現在のオブジェクトが参照できていることがわかります。また参照しているオブジェクトはclick関数の”.”(ドット)の左側にあるmyButtonであることもわかります。
{ content: 'OK', click: [Function: click] }
OK clicked
さらにyourButtonを追加してyourButtonでthisを使い下記を実行してみましょう。
let myButton = {
content: 'OK',
click() {
console.log(this)
console.log(this.content + ' clicked')
},
yourButton: {
content: 'No',
click() {
console.log(this)
console.log(this.content + ' clicked your button')
}
}
}
myButton.click();
myButton.yourButton.click();
先ほどの例では.(ドット)の左側がthisの参照するオブジェクトになっていたので、myButton.yourButton.click()ではclickの左側のyourButtonが参照するオブジェクトになっています。
{
content: 'OK',
click: [Function: click],
yourButton: { content: 'No', click: [Function: click] }
}
OK clicked
{ content: 'No', click: [Function: click] }
No clicked your button
次にmyButtonの内容は最初のものに戻しmyButton.clickを使ってlooseClickを定義し実行します。
const myButton = {
content: 'OK',
click() {
console.log(this)
console.log(this.content + ' clicked');
}
};
const looseClick = myButton.click;
looseClick();
先ほどの処理と同じ結果が出るのではと思ってしまいますがlooseClickを実行した場合は、thisがmyButtonではなくグローバルオブジェクト(ブラウザではWindowオブジェクト)を参照しているため結果はthis.contentがundefinedになってしまいます。
Object [global]
undefine clicked
Button.click()とlooseClick()の実行はコードの流れから同じものだと考えてしまいますがJavaScriptでは異なることに注意する必要があります。

bindの利用方法(1)
2つ目のlooseClickの実行ではthisが参照するものを指定することができれば期待した結果を表示することができます。
その方法としてbindを利用してどのthisを参照するのか指定します。
var myButton = {
content: 'OK',
click() {
console.log(this)
console.log(this.content + ' clicked');
}
};
var boundClick = myButton.click.bind(myButton);
boundClick();
bindの引数にmyButtonオブジェクトを入れることでthisがmyButtonを参照するように設定を行うことができます。thisにMyButtonが入ったため結果は一番最初のコードと同じものになります。
{ content: 'OK', click: [Function: click] }
OK clicked
ここまでの説明でbindがthisに関係していることさらにbindによりthisに指定したオブジェクトを設定できることが理解できたかと思います。
myButtonという同じオブジェクトをbindで利用しているためbindでは同じオブジェクトを指定しないといけないのではという疑問が出てきます。実際に異なるオブジェクトをbindで指定してどうなるか確認してみましょう。
下記のようにyourButtonを新たに定義しcontentプロパティを追加します。
var myButton = {
content: 'OK',
click() {
console.log(this)
console.log(this.content + ' clicked');
}
};
var yourButton = {
content: 'No'
}
var boundClick = myButton.click.bind(yourButton);
boundClick();
実行するとmyButtonのオブジェクトだけではなく他のオブジェクトでもbindで利用できることが確認できます。
{ content: 'No' }
No clicked
bindの利用方法(2)
この例だけではまだ理解が浅い人も多いかと思いますので別の例を使ってbindの設定方法を確認します。greetingを定義し、bind前と後でgreeting関数を実行しています。bindを実行すると新しい関数を作成することができるのでbind後ではbindで作成した新しい関数を実行しています。greeting関数を実行した際にthisは何も指定していないので何が入るかと疑問に持つ人もいるかもしれません。thisに何が入るのか確認してみましょう。
const greeting = function(){
console.log(this)
console.log(this.name);
}
greeting()
const object = {name:"John"}
const new_greeting = greeting.bind(object)
new_greeting()
bindを行っていない場合はthisがグローバルオブジェクトを参照することになり、this.nameはundefinedとなります。そのあとにbindにobjectを設定することでthisの参照先をグローバルオブジェクトからbindで指定したobjectに変更することができます。その結果thisがobjectを参照しているためthis.nameを表示することが可能になります。またbindを利用して新しい関数new_greetingを作成しているので”bindメソッドは、呼び出された際にthisキーワードに指定された値が設定される新しい関数を生成します。”という説明の理解もできるかと思います。
//bind前
Object [global]
// undefined
//bind後
{ name: 'John' }
John
アロー関数を利用した場合のbind
次にgreetingにアロー関数を利用して先ほどと同じことを実行してみましょう。
const greeting = () => {
console.log(this)
console.log(this.name);
}
greeting()
const object = {name:"John"}
const new_greeting = greeting.bind(object)
new_greeting()
アロー関数に変更しても結果は同じだと思っている人も多いかと思いますが、アロー関数ではbindを利用してもthisを変更することはできません。bind前後で結果は同じです。アロー関数ではそれ自身を参照しているので空のオブジェクトが表示されます。
{}
undefined
{}
undefined
thisがgreetingを参照しているのでgreetingにnameを追加します。
const greeting = () => {
this.name = 'Kevin'
console.log(this)
console.log(this.name);
}
greeting()
const object = {name:"John"}
const new_greeting = greeting.bind(object)
new_greeting()
thisがそれ自身を指しているのでbindするしないにかかわらずgreetingの内部で設定したnameのKevinが表示され同じ結果になります。
{ name: 'Kevin' }
Kevin
{ name: 'Kevin' }
Kevin
new_greeting()
Fetch関数を使った例
ここまで読み進めた方であればbindメソッドがどのようなものか少しずつ理解が深まっているかと思います。さらに次はfetch関数を利用した場合のbindの例の説明を行いたいと思います。
ourUsersの中のgetUsersメソッドを利用して外部からユーザ情報を取得してusersの中に取得した配列を保存する処理を実行しています。下記の場合にthisには何が入るのか確認しています。
const ourUsers = {
users: [],
getUsers() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(function (response) {
return response.json();
})
.then(function (users) {
this.users = users;
console.log(this)
})
}
}
ourUsers.getUsers()
上記を実行すると下記の結果となります。この場合thisはourUsersではないのでグローバルオブジェクト(ブラウザの場合はWindowオブジェクト)のためグローバルオブジェクトのusersに取得したデータが保存されます。
this(ourUsersオブジェクト)をbindで指定することでourUsersのusersに保存することができます。
const ourUsers = {
users: [],
getUsers() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(function (response) {
return response.json();
})
.then(function (users) {
this.users = users;
console.log(this)
}.bind(this)) //ここでbindを行う
}
}
ourUsers.getUsers()
bindを利用せずアロー関数を利用することでもbindと同じ結果となります。またアロー関数だとコードもすっきりします。
const ourUsers = {
users: [],
getUsers() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(users => {
this.users = users;
console.log(this)
})
}
}
ourUsers.getUsers()
シンプルな例を3つ利用してbindを説明してきましたがここまでの説明でbindメソッドがどのようなものな完全に理解とは言えなくても理解は進んだのではないでしょうか。thisを利用して意図した通りの動作になっていない場合はthisが何を参照しているか確認することで問題は解決するかもしれません。
bind関数の引数の設定
bindには引数をとることができるのでthisに設定するオブジェクト以外の引数を設定して動作確認をおこないます。bindにまずthisに入るオブジェクトを設定します。2つ目、3つ目の引数には関数の引数に対応するageとheightの値を入れることができます。
const greeting = function(age, height){
console.log(`${this.name}の年齢は${age},身長は${height}です。`)
}
const object = {
name: 'John',
}
const new_greeting = greeting.bind(object,20,'175cm');
new_greeting()
bindの設定した2つ目、3つ目がage, heightに渡され期待した結果が表示されます。
Johnの年齢は20,身長は175cmです。
call関数の使い方
bindを利用した場合は、下記のように新しい関数を作成することができ、その新しい関数を実行していました。
const new_greeting = greeting.bind(object,20,'175cm');
call関数ではbindとは異なり新しい関数を作成するのではなくそのまま実行することができます。
const greeting = function(age, height){
console.log(`${this.name}の年齢は${age},身長は${height}です。`)
}
const object = {
name: 'John',
}
greeting.call(object,20,'175cm'); //call関数
//結果
Johnの年齢は20,身長は175cmです。
bind関数とcall関数の違いが理解できたかと思います。
非常にシンプルなコードを利用しているので実践的ではないかもしれませんが、本文書を通してbind関数がどのようなものかわかってもらえたら大変うれしいです。