Как получить доступ к «этому» внутри обратного вызова

ключевое слово JavaScript this является источником боли для многих людей. Обычный вопрос звучит примерно так:

«Я вызываю функцию Firebase, и внутри этого обратного вызова мне нужно получить доступ к this, чтобы обновить мой компонент React».

Другой распространенный случай — setTimeout:

«Проблема в том, что setTimeout принимает обратный вызов, но мне нужно получить к нему доступ, а setTimeout изменит его!»

Основное руководство

В обычных условиях, this относится ко всему, что находится слева от точки и время звонка. Есть несколько исключений, которые специально изменяют поведение этого—call, apply а также bind. Если вы не использовали одну из этих функций для изменения поведения this, посмотрите слева от точки во время вызова.

Пример

var alarm = {
  ringer: function () {
    console.log("The " + this.color + " alarm: Ring!!!");
  },
  color: "red"
}

alarm.ringer();

Это довольно прямолинейно. Когда вы звоните alarm.ringer();слева от точки alarm. Итак, внутри функции звонка this это сигнал тревоги и цвет красный.

Ошибка в обратном вызове setTimeout

var alarm = {
  ringer: function () {
    console.log("The " + this.color + " alarm: Ring!!!");
  },
  set: function (milliseconds) {
    setTimeout(function () { this.ringer() }, milliseconds);
  },
  color: "red"
}

alarm.set(2000);

Цель здесь заключалась в том, чтобы будильник зазвонил через две секунды. К сожалению, мы получаем эту ошибку:

this-setTimeout.png

Причина в том, что setTimeout вызывает переданный в него обратный вызов. Судя по ошибке, мы видим, что функция setTimeout создала Timeout объект и сохранил функцию обратного вызова, которую мы передали в него, как Timeout._onTimeout. А также в то время это называлось то, что слева от точки, — это тайм-аут, а не тревога.

Классическое решение

Традиционно проблема «этого» решалась путем присвоения «этого» из внешней области видимости новой переменной с именем «_this» или «that».

var alarm = {
  ringer: function () {
    console.log("The " + this.color + " alarm: Ring!!!");
  },
  set: function (milliseconds) {
    var that = this;
    setTimeout(function () { that.ringer() }, milliseconds);
  },
  color: "red"
}

alarm.set(2000);

Теперь это работает. Поскольку значение this внутри set функция-будильник, и that просто обычная переменная. Поскольку функции могут обращаться к переменным из окружающей их области видимости, that.ringer() внутри обратного вызова alarm.ringer().

The red alarm: Ring!!!

Решение стрелочной функции ES6

Стрелочные функции ES6 не работают точно так же как обычные функциональные выражения. У них нет собственных привязок к «этому», поэтому любые ссылки на «это» используют привязки из закрывающей функции.

let alarm = {
  ringer: function () {
    console.log("The " + this.color + " alarm: Ring!!!");
  },
  set: function (milliseconds) {
    setTimeout(() => { this.ringer() }, milliseconds);
  },
  color: "red"
}

alarm.set(2000);

Как упоминается в ссылке MDN выше, стрелочные функции плохо подходят для методов, но они представляют собой удобный способ упростить обратные вызовы. В 2019 году большинство разработчиков JS используют стрелочные функции для решения этой проблемы и сталкиваются только с шаблоном «это = то» в библиотеках и устаревшем коде.

Первоначально опубликовано на 16 апреля 2019 года.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *