Мы часто используем ключевое слово this
в нашем JS-коде, но оно остается одной из наиболее неправильно понимаемых концепций JavaScript. Мы склонны забывать, в какой сфере мы находимся и к чему имеем доступ. Вы когда-нибудь находили ошибку «Cannot read property ‘x’ of undefined
» и все время задавались вопросом, почему так, несмотря на правильную инициализацию всех переменных (если нет, сначала сделайте это: p)? В этой статье мы обсудим, как предотвратить эту ошибку с помощью функции привязки.
Недавно в рамках стажировки я много работал над Angular 5 и TypeScript (следовательно, JavaScript) и столкнулся с различными проблемами. Будучи новичком в JavaScript, борясь с ними и обнаруживая, что коллеги довольно часто сталкиваются с похожими проблемами, я решил написать эту серию, чтобы помочь вам сэкономить свое время, если вы сталкиваетесь с аналогичными ошибками :)
Проблема
Мы используем Closures
почти везде в нашем коде.
Допустим, у нас есть массив объектов, содержащих products
- каждый из которых имеет имя и цену. Теперь мы хотим регистрировать каждый продукт, используя forEach
loop, как показано.
Конечно, вы можете захотеть проделать много сложных вещей с каждой записью массива или даже определить собственный компаратор для sort
function; вместо того, чтобы просто регистрировать записи. Внутри функции обратного вызова forEach
, если мы попытаемся получить доступ к любому из свойств класса Component (что кажется абсолютно нормальным), произойдет следующее:
Это потому, что this
в theforEach
callback указывает не на область AppComponent, а на вновь созданную область.
Решение
Вот уловка!
Обратите внимание на привязку в конце определения закрытия. Нам нужно передать требуемое значение thethis
parameter в целевую анонимную функцию обратного вызова. Это гдеbind()
method появляется на картинке.
Thebind()
method создает новую функцию bound, при вызове которой itsthis
keyword устанавливается равным предоставленному значению.
function
.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg
- это значение, которое будет передано в качествеthis
параметра целевой функции при вызове связанной функции. arg1, arg2, …
- это аргументы, добавляемые к аргументам, предоставленным связанной функции при вызове целевой функции. Эти аргументы (если таковые имеются) затем вставляются в начало аргументов, передаваемых целевой функции, за которыми следуют аргументы, передаваемые связанной функции.
Запутались? Начнем с основ.
Дайвинг глубоко
- Область действия: Область действия определяет доступность (видимость) переменных. JavaScript имеет область видимости функции: каждая функция создает новую область видимости.
- Замыкание: Замыкание - это комбинация функции, объединенной (заключенной) со ссылками на ее окружающее состояние (лексическое окружение). Это внутренняя функция, которая имеет доступ к переменным внешней функции.
Я использовал JavaScript в приложении Angular, поэтому this
относится к области действия AppComponent (в отличие отwindow
object в чистом приложении JS).
Если мы запускаемconsole.log(this)
any где-нибудь внутри вышеуказанного Компонента (конструктора, ngOnInit,
и т. д.), он регистрирует объект AppComponent.
Теперь, если мы запустим то же самое внутри функции обратного вызова inforEach
loop, this
относится к вновь созданной области (которая равна undefined
).
Таким образом, когда мы dofunction.bind(this)
, мы привязываем требуемый контекст к целевой функции, чтобы его можно было использовать внутри нее. Нам нужно выполнять привязку контекста только тогда, когда нам требуется доступ к переменным компонента (в случае приложения Angular) внутри замыканий.
Альтернативы
Этот вариант использования довольно распространен, и есть разные this
способы заставить его работать!
- Установка значения
this
вне закрытия
Мы можем установить переменнуюthis
в любую переменную вне анонимной функции обратного вызова, а затем использовать ее внутри.
Но это становится беспорядочным и заполняет код ненужными определениями переменных, таких как self
или that
.
2. Стрелочные функции
Вместо использования анонимной лямбда-функции мы можем использовать стрелочную функцию. У выражения функции стрелки нет собственного this
. На введение стрелочных функций повлияли два фактора: более короткие функции и отсутствие привязки this
.
Wohoo! Теперь вы поняли и исправили ошибку.
Надеюсь, это вам помогло! Если да, улыбнитесь и передайте! :)