結論
最終的にtypescriptは関係がない。
Function.bind()
で参照したいオブジェクトをバインドさせるとスマート。
背景
クラスメソッドでEventTargetに追加したListenerを削除する必要ができたため、変更したが、期待した挙動をしなかった。
元の状態
class Temp{
private state: boolean
constructor(){
this.state = true;
}
public start(): void{
...
document.addEventListener('keydown', (e)=>{
...
switch(e.code){
case...:
this.move();
break;
}
});
};
...
}
Listenerを削除するため、無名関数を関数へ変更
EventTargetに追加されるEventListenerは関数の参照が渡されるため、addEventListener内で無名関数を定義すると参照が保持されない。
そのため、EventTargetに追加された無名関数は後々削除することができない。
Listenerを削除する必要がある場合は、 基本的に、addEventListener内で無名関数を記述しないほうが良さそう。
public start(): void{
...
document.addEventListener('keydown', this.moveCtrl);
}
public moveCtrl(e: KeyBoardEvent): void{
...
switch(e.code){
case...:
this.move();
break;
}
};
thisの参照問題
ここでthisはどこを参照してくるか?
// 実行時
this.moveCtrl => Temp.moveCtrl
this.state => document.state() // undefined
this.move => document.move() // undefined
// 理想
this.moveCtrl => Temp.moveCtrl
this.state => Temp.state()
this.move => Temp.move()
moveCtrl()内のthisの参照をTempにさせる
thisをTempにさせるには以下の2つ
- リスナーにオブジェクトとして渡す。
- 渡す関数にTempをバインドする。
1. リスナーにオブジェクトとして渡す。
リスナーにオブジェクトを渡した場合、あらゆるイベントを捕捉する handleEvent()
を指定することで実現できる。
ただし、イベントタイプで実行するメソッドを分けたい場合、handleEvent()
内で条件分岐させる必要がありそう。
public start(): void{
...
document.addEventListener('keydown', this);
}
// public moveCtrl(): void{
public handleEvent(): void{
...
if(this.state){
switch(e.code){
case...:
this.move();
break;
}
}else{...}
};
2. 渡す関数にTempをバインドする。
Function.prototype.bind()
で関数内のthisの参照を指定することができる。
constructor(){
this.moveCtrl = this.moveCtrl.bind(this)
}
public start(): void{
...
document.addEventListener('keydown', this.moveCtrl);
}
public moveCtrl(e: KeyBoardEvent): void{
...
switch(e.code){
case...:
this.move();
break;
}
};
番外
初めにいろいろこねくり回した結果。
type HandleEvent = {
handleEvent: Function,
manager: Temp,
}
class Temp{
private state: boolean
private handleEvent: HandleEvent | EventListenerOrEventListenerObject;
constructor(){
this.state = true;
this.handleEvent = {
handleEvent: this.moveCtrl,
manager: this,
}
}
public start(): void{
document.addEventListener("keydown", (this.handleEvent as EventListenerOrEventListenerObject));
}
public moveCtrl(e: KeyBoardEvent): void{
...
switch(e.code){
case...:
(this as any as HandleEvent).manager.move();
break;
}
};
}
参考文献
https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener
https://note.com/yamanoborer/n/n2e4cc40328b7