Контекст или this - это способ связать функцию с объектом. В Function Declaration (function foo(){}) this вычисляется в момент вызова и равен объекту перед точкой. Если такого объекта нет — тогда this будет указывать на глобальный контекст (window). Но есть главное правило this всегда указывает на объект.
Способы связывания:
- Связывание по умолчанию;
- Явное связывание;
- Неявное связывание;
- Связывание через new.
Контекст по умолчанию
В этом случае контекст - это глобальный this или undefined. В зависимости от среды this будет ссылаться на глобальный объект или на undefined, если включен строгий режим.
globalthis - получение универсального доступа к глобальному объекту windows в браузере и к global в node.
Неявное связывание
Связывание контекста происходит при одновременном вызове объекта и метода, но если сохранить метод объекта где-нибудь еще и попробовать его вызвать, то контекст потеряется.
Явное связывание (call/apply/bind)
call - передается объект с которым планируется связывание и аргументы функции;
apply - передается объект для связывания и массив аргументов функции, которые автоматически распакуются в функцию.
bind - не вызывает функцию сразу. Вместо этого он возвращает другую функцию — связанную с указанным контекстом навсегда. Контекст у этой функции изменить невозможно.
Заимствование метода
У объекта objB отсутствует метод "say()". Однако, можно заимствовать этот метод у объекта objA, изменив контекст метода с объекта A на В.
Объект obj похож на массив (псевдомассив), можно скопировать некоторые методы из Array в этот объект (первый вариант). Это работает, потому что для внутреннего алгоритма встроенного метода join важны только корректность индексов и свойство length, он не проверяет, является ли объект на самом деле массивом. И многие встроенные методы работают так же.
Альтернативная возможность – можно унаследовать от массивов их методы, установив obj.__proto__ как Array.prototype, таким образом все методы Array станут автоматически доступны в obj.
Связывание через new
Функция может быть не просто функцией, но функцией-конструктором. Важной отличительной ее чертой является то, что она называется с заглавной буквы и вызывается вместе с new, а также использует свойство prototype.
Когда вызывается функция-конструктор создается новый объект, выставляется служебное свойство prototype. Новый объект назначается в качестве this и неявно возвращается из функции. Если функция возвращает свой объект, то новый объект отбрасывается. Таким образом, функция-конструктор всегда имеет контекст, отличный от глобального.
Приоритетность связывания
1. Связывание через new;
2. Явное;
3. Неявное;
4. По умолчанию.
Проблема потери контекста
Проблема потери контекста, или проблема с this, возникает в JavaScript, когда функция вызывается в контексте, который отличается от ожидаемого. В этом случае, проблема связана с тем, как контекст (this) обрабатывается внутри функции callback, переданной в setTimeout.
Разбор кода:
- Создается объект object, который содержит свойство value со значением 42 и метод method.
- В методе method объявляется переменная callback, которая содержит функцию без явного указания контекста (this). Таким образом, контекст функции callback будет определен на момент ее выполнения.
- Затем вызывается метод object.method(). В этот момент метод method выполняется в контексте объекта object, и this внутри method ссылается на этот объект.
- Устанавливается таймер с помощью setTimeout, передавая ему функцию callback для выполнения через 200 миллисекунд.
- Проходит 200 миллисекунд, и функция callback выполняется, но в этот момент контекст (this) уже не связан с объектом object. Вместо этого он связан с глобальным объектом (обычно window в браузере), поскольку функция setTimeout устанавливает контекст по умолчанию на глобальный объект.
- Поэтому при попытке вывести this.value внутри callback, this указывает на глобальный объект (обычно window), который не имеет свойства value, поэтому выводится undefined.
Для решения этой проблемы можно воспользоваться следующими подходами для решения:
Подходы для решения
- Принять, что есть контекст и при необходимости использовать bind/apply/call
2. Использовать лексическую область видимости: использовать замыкание и сохранять контекст в дополнительной переменной или использовать стрелочные функции.