前回の続き。
前回の例と違うのは、クラス内からsetTimeout
を呼んでるのと、オブジェクト内のオブジェクトのメソッドを呼んでること。
setTimeout関数の失敗例 その2
<script>
document.addEventListener('DOMContentLoaded', function () {
'use strict';
class Test {
constructor() { this.value = "成功"; }
alert() {
alert(this.value);
}
}
class TestWrapper {
constructor() {
this.test = new Test(); //オブジェクト内にオブジェクトを作成
}
setTimeoutWrap() {
setTimeout(this.test.alert.bind(this), 100); //失敗例
}
}
const testwrapper = new TestWrapper(); //オブジェクトを作成
test01.addEventListener('click', function () {
testwrapper.setTimeoutWrap();
});
});
</script>
<button id="test01">test01</button>
このプログラムを実行すると
undefinedが返ってきた。
成功例
どのように書けばいいかというと、setTimeout(this.test.alert.bind(this.test), 100);
と書けば成功。setTimeoutで呼び出すメソッドが連鎖する場合、bindするオブジェクトも連鎖させなければならないようだ。
オブジェクトの4段連鎖なら、setTimeout(this.test.test2.test3.alert.bind(this.test.test2.test3), 100);
となる 。
setTimeout(function () { this.test.alert() }.bind(this), 100);
と書いても成功。前回書いたように、括弧を付けないとオブジェクトのメソッドが関数化するので、括弧を付けて関数化させないほうがいい。オブジェクトの4段連鎖なら、setTimeout(function () { this.test.test2.test3.alert() }.bind(this), 100);
となってbind(this)
1つで済むので。
@cfm-artさんのコメントのように、setTimeout(() => { this.test.alert() }, 100);
と書いても成功。アロー関数は関数にbind(this)
を付ける効果があるようだ。
class内メソッドのアロー関数化
アロー関数にすればそれが定義されている場所でのthisになるので、class内メソッドをアロー関数にできないか考えてみた。
<script>
document.addEventListener('DOMContentLoaded', function () {
'use strict';
class Test {
constructor() {
this.value = "成功";
this.alert = () => {
alert(this.value);
}
}
}
class TestWrapper {
constructor() {
this.test = new Test(); //オブジェクト内にオブジェクトを作成
}
setTimeoutWrap() {
setTimeout(this.test.alert, 100); //成功
}
}
const testwrapper = new TestWrapper(); //オブジェクトを作成
test01.addEventListener('click', function () {
testwrapper.setTimeoutWrap();
});
});
</script>
<button id="test01">test01</button>
constructor内にメソッドを書く書き方が美しくないが、これはいいかもしれない 。
オブジェクトは本来こうあるべきじゃあないだろうか。
この書き方でできなくなることもある。呼び出し側で関数にbind(new_this)
とやってアロー関数内のthis
の値を書き換えられなかった。bind関数よりもアロー関数のほうが強い。
class内メソッドのアロー関数化(プロトタイプ対応版)
class Test {
constructor() {
this.value = "成功";
Test.prototype.alert = () => {
alert(this.value);
}
}
}
コメントの指摘を受け、メソッドをプロトタイプに入れるように改善してみた。オブジェクトのインスタンス毎にメソッドを作るよりもプロトタイプ一箇所で作ったほうがいい。
class Test {
constructor() {
this.value = "成功";
Test.prototype.alert = function() {
alert(this.value);
}.bind(this);
}
}
アロー関数を使わずに書くことも可能だった。