Это «это», но что именно означает — зависит от контекста
Ключевое слово this — одна из особенностей JavaScript. Изначально оно пришло из Java, чтобы помочь в реализации объектно-ориентированного программирования. А особенность в том, что это слово может означать разные объекты в зависимости от того, где оно написано. Разбираемся, как это работает, на примерах с кодом.
Что такое this
В JavaScript this — это ссылка на какой-то объект. Особенность в том, что объект, на который ссылается this, может меняться в зависимости от контекста вызова. Это как показать пальцем на что-то в коде и сказать «Вот, я имею в виду вот этот объект». Хитрость в том, что «вот это» в коде может быть разным — и всё зависит от того, кто хочет использовать этот объект.
Чаще всего this определяется тем, кто вызывает функцию. В этом его отличие от области видимости, которая определяется местом вызова функции. Из-за этого в коде часто можно встретить много вызовов this, только ссылаться они будут на разные объекты.
Теперь посмотрим, как меняется значение this в разных ситуациях: от простого вызова без контекста до сложных функций.
Простой вызов
В общем случае this указывает на глобальный объект. Для браузера такой объект — это само окно браузера, поэтому если набрать в консоли браузера
console.log(this);
то мы увидим, что здесь this привязано к глобальному контексту, а именно к объекту window в браузере.
Когда в JS включён строгий режим, то правила меняются. Если функция вызывается в строгом режиме use strict, то this будет равно undefined. Про строгий режим поговорим в другой раз; если что, по умолчанию он выключен, поэтому this можно использовать даже в простом вызове.
This ссылается на объект
Когда this используется внутри объекта, то ссылается на сам объект. Допустим, мы создали объект dog с тремя методами и используем в одном из его методов this. Так как this внутри метода указывает на текущий объект, в контексте которого метод был вызван, то в нашем примере мы получим ссылку на объект dog.
Напишем простой код с объектом и выведем в консоль значение this:
В консоли мы увидим представление объекта со всеми методами:
Использование this делает код более гибким. Значение this само привязывается к объекту, в контексте которого он вызван. Это позволяет методу работать с данными конкретного экземпляра объекта и делать код более универсальным.
Режим конструктора
Конструктор — это функция, которую используют, чтобы создавать однотипные объекты. Название конструктора в JavaScript должно быть существительным и начинаться с большой буквы. Конструкторы вызывают с помощью ключевого слова new. Задача конструктора — объяснить компьютеру, с какими параметрами и свойствами нужно будет создавать новый объект.
Сделаем конструктор и создадим новый объект. Обратите внимание, как мы используем this: оно автоматически указывает на объект в текущем конструкторе, откуда его вызывают:
Такой подход удобен тем, что с помощью this и единственной функции-конструктора мы можем создавать множество однотипных объектов и указывать напрямую, к какому объекту обращаемся.
Покажем, как это работает, на примере функции-конструктора для создания объектов автомобилей:
Если бы мы писали подобную логику без использования this, то сначала бы создали новый объект, установили его свойства, вернули объект и уже после этого создавали новые. Тогда код получился бы более громоздким.
Работа с методами call() и apply()
В JS объекты чаще всего обладают собственными свойствами и методами, при этом разные объекты не могут просто так использовать методы друг друга. Но иногда нужно обойти это ограничение. Для этого используют методы call() и apply() — они позволяют вызывать нужные функции в контексте других объектов. Чтобы передать нужный контекст в функцию, как раз и используют this — он хранит контекст вызова, который не меняется во время передачи в другую функцию.
Вызов функции через методы call() или apply() называют непрямым вызовом.
В синтаксисе методов есть небольшое различие: call() принимает аргументы списком через запятую, apply() принимает массив аргументов. В остальном они работают плюс-минус одинаково.
Для наглядности сделаем так:
- Создадим функцию, которая берёт в качестве аргумента приветственное слово и смайлик.
- Внутри этой функции используем this — это поможет нам получить контекст того объекта, который вызвал эту функцию.
- Создадим две переменные с именами.
- Вызовем функцию приветствия, используя переменные с именами как аргументы.
- Так как мы использовали this внутри функции, она получит нужный контекст: перенесёт его из переменных с именами.
- В итоге функция приветствия по контексту поймёт, что нам нужно взять имена из переменных и добавить их в вывод в консоль.
Стрелочные функции
Стрелочная функция не создаёт свой контекст исполнения, а берёт его из своей внешней функции, в которой эта стрелочная функция определена.
Создадим функцию, которая выводит приветствие и имя пользователя, а затем спустя некоторое время повторяет приветствие:
Если бы мы использовали обычную функцию, то контекст бы потерялся и this уже не указывало на объект user, и тогда пришлось бы использовать непрямой вывоз. По этой причине this часто используют в стрелочных функциях, чтобы не перегружать код дополнительными методами.
Как понять, чему равно значение this
Определяем, чему равно this в каждом конкретном случае:
- Если мы не находимся внутри функции, то this равно глобальному объекту — окну браузера.
- Если функция получена как свойство объекта и сразу же используется, то this будет равно этому объекту.
- Если функция вызывается с помощью оператора new, то this будет ссылаться на вновь созданный объект в конструкторе функции
- Если функция создана с помощью метода call или apply, то значение this будет аргументом этой функции
- Если мы внутри стрелочной функции, то this равно значению this, находящемуся вне этой функции.
Зачем такие сложности?
Ключевое слово this хорошо раскрывает динамическую природу JavaScript, даёт большую гибкость и сокращает дублирование кода. Например, с помощью this можно писать очень абстрактные функции, которые будут использовать контекст выполнения для своей работы. Так мы можем добиться полиморфизма, а оттуда уже до ООП недалеко.