Возможно, самое страшное ключевое слово в JS, давайте вместе победим этого монстра.
Одним из самых запутанных механизмов в JavaScript является ключевое слово this
. Даже опытный разработчик иногда может растеряться. Итак, давайте посмотрим, действительно ли это так сложно!
this
- это специальное ключевое слово, которое автоматически определяется в области действия каждой функции. Но как движки JavaScript решают, какое значение следует присвоить this
.
Привязка по умолчанию
В привязке по умолчанию this
относится к глобальному объекту. Любой простой вызов функции без декорирования вызовет привязку по умолчанию, если область, в которой используется this
, не использует strict mode
.
Рассмотреть возможность:
Переменные, объявленные в глобальной области как var a = 2
, function foo() {…}
, являются синонимами одноименных свойств глобального объекта. Они не копии друг друга, они друг друга. В этом фрагменте foo()
вызывается с простой ссылкой на функцию без декорирования. Следовательно, здесь применяется привязка по умолчанию, то есть глобальный объект.
Если действует strict mode
, глобальный объект не подходит для привязки по умолчанию, поэтому для this
вместо этого устанавливается значение undefined
.
Хотя общие правила привязки this
полностью основаны на сайте вызова, глобальный объект имеет право на привязку по умолчанию только в том случае, если содержимое foo()
не выполняется в strict mode.
Состояние strict mode
сайта вызова foo()
не имеет значения.
Неявная привязка
Неявная привязка вызывается, если сайт вызова имеет объект контекста. А теперь что такое объект контекста!
Рассмотреть возможность:
Здесь сайт вызова использует obj
контекст для ссылки на функцию, поэтому мы можем сказать obj
«владеет» или «содержит» ссылку на функцию во время вызова функции.
Теперь рассмотрим это:
Это может показаться запутанным, но для сайта вызова имеет значение только последний уровень цепочки ссылок на свойства объекта. Итак, this
в foo()
относится к obj
для вышеуказанного вызова функции.
Попался
Рассмотрим следующий код:
Хотя bar
кажется ссылкой на obj.foo
, на самом деле это просто еще одна ссылка на сам foo
. Более того, сайт вызова имеет значение, а сайт вызова - это bar()
, который является простым вызовом без декорирования, и, таким образом, применяется привязка по умолчанию.
Есть еще один способ потерять неявную привязку, т.е. когда мы используем обратные вызовы. Вы не знаете, как переданная функция вызывается в обратном вызове. Проблемы с доверием 😐️
Являются ли привязки по умолчанию и неявная привязка одинаковыми?
Рассмотрим следующий код:
Как обсуждалось ранее, переменные, объявленные в глобальной области видимости, являются синонимами одноименных свойств глобального объекта. Мы можем обозначать эти переменные как window.a
, window.foo
соответственно .
Поэтому всякий раз, когда мы используем простые вызовы функций без декорирования, это означает, что его контекстный объект равен window
, и, следовательно, в этих случаях мы получаем window
как this
.
Явная привязка
Теперь давайте возьмем управление в свои руки 😎️.
Для функций доступны некоторые утилиты (через их [[Prototype]]
), которые могут быть полезны для этой задачи. В частности, методы bind(…)
, call(…)
и apply(…)
.
call (), apply () и bind ()
- Мы можем указать, что мы хотим, чтобы наш this использовал эти функции.
- Одна общая вещь в них - это то, что он принимает первый параметр, объект, который назначается как
this
при вызове функции. call()
&apply()
немедленно вызывает функцию, аbind()
просто возвращает новую функцию.
Это очень полезные функции в JavaScript, которые не могут быть рассмотрены в этой публикации, поэтому мы подробно поговорим об этих функциях в другой статье.
новая привязка
Когда функция вызывается с new
перед ней, происходят следующие вещи:
- Совершенно новый объект создается из воздуха.
- Вновь созданный объект устанавливается как
this
привязка для вызова этой функции.
new foo(2)
вернет {a:2}
в приведенном выше фрагменте.
Происходит еще несколько вещей, но это выходит за рамки данной темы.
Заключение
Как только вы знаете сайт вызова функции, к сайту вызова можно применить 4 правила в этом порядке приоритета для определения this
привязки:
- Вызывается с
new
? Используйте только что построенный объект. - Вызывается с
call(),apply(), bind()
? Использовать указанный объект. - Вызывается с помощью объекта контекста, которому принадлежит вызов? Используйте этот объект контекста.
- По умолчанию:
undefined
вstrict mode
, в противном случаеglobal
.
Для более глубокого понимания this
я настоятельно рекомендую прочитать этот & ПРОТОТИПЫ ОБЪЕКТОВ серии ВЫ НЕ ЗНАЕТЕ JS, написанную Кайлом Симпсоном.