case
クラスで定義されたメソッドをsetTimeoutから呼び出したときのthisの取り扱いについて。
- Personクラスにhelloメソッドが定義されている。
- Personクラスをインスタンス化してbob変数に代入している。
- 変数bobをsetTimeoutで1秒後に実行するようになる。
- 期待値としては、hello Bobと出力したいがhelloだけ出力されてしまう。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
hello() {
console.log('hello ' + this.name);
}
}
const bob = new Person('Bob', 23);
setTimeout(bob.hello, 1000);
//実行結果
>>> hello
結論
bob.helloはPersonオブジェクトのhello()関数だけを実行していて、hello()メソッドにはPersonオブジェクトが渡されていないため、Bobにあたるthis.nameが
実行されない。
なぜか?
前提:javascriptの仕様なので受け入れるしかない。
- 関数として実行されるthisはwindowオブジェクトを参照先となる。
- 例としてwindowオブジェクトにnameプロパティを追加して"stan"を代入してみる。
- するとhello stanと表示された。すなわちthisはwindowオブジェクトを参照している。
//例としてwindowオブジェクトにnameプロパティを追加して"stan"を代入してみる。
window.name = "stan"
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
hello() {
console.log('hello ' + this.name);
}
}
const bob = new Person('Bob', 23);
setTimeout(bob.hello, 1000);
//hello stanと表示された。すなわちthisはwindowオブジェクトを参照している。
>>> hello stan
- これはsetTimeoutでbob.helloと実行した時にPersonオブジェクトのhelloを参照しているわけではない。
- javascriptは、メモリ空間でPersonのhello()関数を
コピーして、コピーをしたhello()関数を参照しているため、Personオブジェクトのプロパティを受け取れていないために起こる。 - メモリ消費効率を考えた上の仕様なのでしょう。
- そのため、Personオブジェクトを格納しているbob変数を束縛する必要がある。
どのように?
- bind関数を使用する。
- bind関数にbobオブジェクトを渡すとPersonオブジェクトを束縛することができる。
window.name = "stan"
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
hello() {
console.log('hello ' + this.name);
}
}
const bob = new Person('Bob', 23);
setTimeout(bob.hello.bind(bob), 1000);
>>> hello bob
ややこしいが、こういうものだと、受け入れる。