Таймеры и потеря контекста в JavaScript
При использовании таймеров в обработчиках событий нас поджидают проблемы с потерей контекста. Давайте посмотрим на примере.
Пусть у нас есть инпут:
<input id="elem" value="text">
Пусть по клику на этот инпут сработает анонимная функция и внутри этой функции запустится таймер, каждую секунду выводящий что-нибудь в консоль:
let elem = document.querySelector('#elem');
elem.addEventListener('click', function() {
setInterval(function() {
console.log('!!!'); // что-нибудь выводим в консоль
}, 1000);
});
Пока все работает верно. Но пусть теперь
мы хотим выводить в консоль value
нашего инпута - нас ждет сюрприз: в консоль
будет выводится undefined
:
elem.addEventListener('click', function() {
setInterval(function() {
console.log(this.value); // будет выводится undefined
}, 1000);
});
Все дело в том, что у нас получается функция
в функции: есть внешняя анонимная функция,
которая вызывается по клику и внутренняя
анонимная функция, которую запускает таймер.
Во внешней функции this
указывает
на инпут, но во внутренней - нет. Имеет место
потеря контекста.
Почему выводится undefined
, а не вываливается
ошибка в консоль, как это было в предыдущих
уроках? Потому что this
внутри функции,
вызываемой через setInterval
, указывает
на window
.
Это значит, что мы пытаемся прочитать свойство
value у объекта window
, вот так: window.value
,
а такого свойства в нем нет, и мы получаем
undefined
(не ошибку).
Поправим проблему введением self
:
elem.addEventListener('click', function() {
let self = this;
setInterval(function() {
console.log(self.value);
}, 1000);
});
Пусть дан такой код:
<input type="button" id="elem" value="1">
let elem = document.querySelector('#elem');
elem.addEventListener('click', function() {
setInterval(function() {
this.value = Number(elem.value) + 1;
}, 1000);
});
Автор кода хотел, чтобы по нажатию на кнопку,
значение этой кнопки каждую секунду увеличивалось
на 1
. Однако, по нажатию на кнопку
вообще ничего не происходит. Исправьте ошибку
автора кода. Напишите текст, в котором вы
дадите объяснение автору кода, почему возникла
его ошибка.