JavaScriptにおけるタイマーとコンテキストの喪失

イベントハンドラー内でタイマーを使用する際には、 コンテキスト喪失の問題が待ち構えています。 例を見てみましょう。

次のような入力フィールドがあるとします:

html<input id="elem" value="text">

この入力フィールドをクリックした際に匿名関数が実行され、 その関数内でタイマーが起動し、毎秒何かをコンソールに 出力するようにしましょう:

javascriptlet elem = document.querySelector('#elem');

elem.addEventListener('click', function() {
  setInterval(function() {
    console.log('!!!'); // コンソールに何かを出力
  }, 1000);
});

ここまでは正常に動作します。しかし、今度はコンソールに 入力フィールドの value を出力したいとすると、 驚きが待っています:コンソールには undefined が出力されます:

javascriptelem.addEventListener('click', function() {
  setInterval(function() {
    console.log(this.value); // undefinedが出力される
  }, 1000);
});

問題は、関数内関数ができてしまうことです。 クリック時に呼び出される外側の匿名関数と、 タイマーによって起動される内側の匿名関数があります。 外側の関数では this は入力フィールドを指しますが、 内側の関数ではそうではありません。コンテキストの喪失が 発生しています。

なぜ、以前のレッスンで起こったようにコンソールエラーに ならないで undefined が出力されるのでしょうか? それは、setInterval 経由で呼び出される関数内の this が window を指すためです。

つまり、window オブジェクトから value プロパティを 読み取ろうとしているのです。このように:window.value。 window オブジェクトにはそのようなプロパティは存在しないため、 (エラーではなく)undefined を得るのです。

self を導入してこの問題を修正しましょう:

javascriptelem.addEventListener('click', function() {
  let self = this;
  
  setInterval(function() {
    console.log(self.value);
  }, 1000);
});
Задание 1

次のようなコードがあるとします:

html<input type="button" id="elem" value="1">
javascriptlet elem = document.querySelector('#elem');

elem.addEventListener('click', function() {
  setInterval(function() {
    this.value = Number(elem.value) + 1;
  }, 1000);
});

このコードの作者は、ボタンが押されると、 そのボタンの値が毎秒 1 ずつ増加することを 期待していました。しかし、ボタンを押しても 何も起こりません。作者のコードの誤りを修正し、 なぜそのエラーが発生したのかを作者に説明する 文章を書いてください。