Передача правильного контекста в setTimeout: пример на JS
Пройдите тест, узнайте какой профессии подходите
Быстрый ответ
Чтобы сохранить контекст this
, можно воспользоваться стрелочной функцией в роли колбэка для setTimeout
:
setTimeout(() => this.myMethod(), 1000);
В качестве альтернативы можно привязать контекст с помощью bind
к обычной функции:
setTimeout(this.myMethod.bind(this), 1000);
"Трудности" идентификации "this"
В JavaScript ключевое слово this
зачастую ставит разработчиков в затруднительное положение. Особенно они встречаются при использовании setTimeout
: this
внезапно превращается в глобальный объект, вместо того чтобы ссылаться на прежний контекст.
Захват контекста с помощью замыкания
Один из способов решить данную проблему – сохранить текущее значение this
, воспользовавшись замыканием, и в дальнейшем использовать его в теле функции, которую передаем в setTimeout
:
var that = this;
setTimeout(function() {
that.myMethod();
}, 1000);
Использование Bind для сохранения контекста
Если вы думаете о совместимости со старыми версиями браузеров, например, Internet Explorer, Function.prototype.bind
придет к вам на помощь. Он позволяет явно привязать контекст к функции, несмотря на попытки setTimeout
его поменять:
setTimeout(this.myMethod.bind(this), 1000);
Дополнительные параметры в setTimeout и контекст
С появлением HTML5 спецификация setTimeout
дополнилась возможностью передачи параметра this
вместе с указанием временного интервала:
setTimeout(function(a) {
this.myMethod();
}, 1000, this);
Однако, стоит помнить, что не все старые версии браузеров поддерживают данную возможность, так что проверьте совместимость.
Помощники из библиотек
Библиотеки вроде jQuery и lodash предлагают свои методы для сохранения контекста this
при использовании setTimeout
. Примерами служат методы $.proxy()
для jQuery и _.bind()
для lodash.
Обход ограничений в старых версиях IE
К сожалению, старые версии Internet Explorer нередко испытывают трудности с методом bind
. В таких случаях, использование замыкания или методов из библиотек, таких как jQuery, может стать единственной возможностью.
Вопросы производительности
Между упомянутыми методами есть незначительные различия в производительности. Они не влияют на setTimeout
, но замыкания могут потреблять больше памяти, чем bind
или стрелочные функции, в связи с созданием дополнительной области видимости.
Визуализация
Представьте, что наша функция – это корабль, который должен оставаться в своем контексте this
. Для этого мы можем использовать следующие инструменты:
1. Привязываем через Bind:
setTimeout(function() {
this.navigate();
}.bind(ship), 1000);
2. Используем стрелочную функцию как компас:
setTimeout(() => {
ship.navigate();
}, 1000);
3. Сохраняем текущий контекст this
:
var currentContext = this;
setTimeout(function() {
currentContext.navigate();
}, 1000);
В зависимости от ситуации выбирайте подходящий инструмент для сохранения контекста вашей функции (this
).
Продвинутые "подводные камни"
Неявная и явная привязка
Иногда мы ошибочно полагаем, что переопределение контекста this
с помощью bind
, .call()
или .apply()
возвращает его в первоначальное состояние. Однако, переопределение не ведет к возвращению к исходному контексту.
Особенности стрелочных функций
Стрелочные функции не поддерживают привязку с помощью bind
, call
или apply
, так как они уже фиксируют свой контекст при объявлении.
Совместимость с транспиляторами и полифилами
Помните, что при использовании нового синтаксиса setTimeout
транспиляторы и полифилы могут не поддерживать его.