今更ですがJavascriptのクラスについて勉強中です。
フォーム値を変更した場合にページ遷移確認ウィンドウ出す処理を汎用的にクラス化しようとしました。
その中で、イベントハンドラにクラス内関数を指定したらインスタンス内のオブジェクトにthisでアクセスできなくなるためクラスに定義した関数が使えない問題が発生しました。
そこそこ上手く解決できたと思うので記事にします。
最初に作ったクラス
class TransitionObserver {
constructor(){
this.InitValueMap = new Map();
this.id = Math.random().toString(36).slice(-16);
}
addPattern(elementPattern) {
const map = new Map();
$(elementPattern).each(function (i, e) {
const element = $(e);
const valueObj = {};
valueObj.name = element.attr("name");
valueObj.val = element.val();
valueObj.checked = element.is(':checked');
map.set(element, valueObj);
})
this.InitValueMap.set(elementPattern, map);
}
start() {
$(window).on('beforeunload.pagetransition' + this.id, this.pageTransitionEvent);
}
stop() {
$(window).off('beforeunload.pagetransition' + this.id);
}
hasUpdateElement() {
for(let mapEntry of this.InitValueMap) {
for(let valueEntry of mapEntry[1]) {
const element = valueEntry[0];
const valueObj = valueEntry[1];
if (valueObj.val != element.val() || valueObj.checked != element.is(':checked')) {
return true;
}
}
}
return false;
}
pageTransitionEvent(event) {
if (this.hasUpdateElement()) { // ←このhasUpdateElementがundefinedになる
event.preventDefault();
return false;
}
}
}
コメントに書きましたがイベントハンドラからコールバックしたpreTransitionEventからhasUpdateElementが見えません。
これはイベントハンドラから呼ばれたときにthisがTransitionObserverのインスタンスではなく別のオブジェクトになっているためです。
イベントハンドラのコールバックメソッドでthisを使うなという話は多くされているのですが、クラス定義したときにthisを使うなってのは無理な話なので何とかする必要があります。
何とかした
class TransitionObserver {
start() {
$(window).on('beforeunload.pagetransition' + this.id, this.pageTransitionEvent(this));
}
pageTransitionEvent(me) {
return function (event) {
if (me.hasUpdateElement()) {
event.preventDefault();
return false;
}
};
}
// その他関数変更無し
}
そういえば関数のカリー化っていう手法で任意の引数を関数内で使えたよなっていう記事を読んだのを思い出して、上記のように実装。
無事に想定通りの動作をするようになりました。
関数をオブジェクトとして使えるのはやっぱり便利ですね。
参考文献
3歳娘「パパ、関数をカリー化して?」
https://qiita.com/Yametaro/items/99cc1c8ebcfc703b1410
JavaScriptでコールバック関数にあらかじめ引数を渡したい!
https://qiita.com/Lewuathe/items/5827a9b429aa71c4f76e
未勉強な独り言:TypeScriptならこんなん気にせずできるんちゃう?知らんけど。