Context (this)
Поведение ключевого слова this
в JavaScript несколько отличается от остальных языков. Имеются также различия при использовании this
в строгом и нестрогом режиме.
В большинстве случаев значение this
определяется тем, каким образом вызвана функция. Значение this
не может быть установлено путем присваивания во время исполнения кода и может иметь разное значение при каждом вызове функции. В ES5 представлен метод bind
, чтобы определить значение ключевого слова this независимо от того, как вызвана функция. Также в ECMAScript 2015 представлены стрелочные функции, this которых привязан к окружению, в котором была создана стрелочная функция.
Глобальный контекст
В глобальном контексте выполнения (за пределами каких-либо функций)this
ссылается на глобальный объект вне зависимости от использования в строгом или нестрогом режиме.
В контексте функции
В пределах функции значение this
зависит от того, каким образом вызвана функция.
Простой вызов
В этом случае значение this
не устанавливается вызовом. Так как этот код написан не в строгом режиме, значением this
всегда должен быть объект, по умолчанию - глобальный объект.
В строгом режиме, значение this
остается тем значением, которое было установлено в контексте исполнения. Если такое значение не определено, оно остается undefined.
Итак, в строгом режиме, если this не определено, оно остается не определено.
Для того, чтобы передать значение this от одного контекста другому, необходимо использовать call или apply.
Стрелочные функции
В стрелочных функциях this
привязан к окружению, в котором была создана функция. В глобальной области видимости this
будет указывать на глобальный объект.
Неважно, как функция foo()
будет вызвана, ее this будет указывать на глобальный объект.this
будет сохранять свое значение, даже если функция foo()
будет вызвана как метод объекта (что в обычных функциях связывает this
с объектом вызова) или с использованием методов call
,apply ,
илиbind
:
Независимо от этого, this
функции foo()
имеет тоже значение, что и при создании функции (глобальный объект в примере выше). То же самое касается стрелочных функций, созданных внутри других функций: их this
будет привязан к окружению.
В примере выше, функция (назовем ее анонимной функцией A), присвоенная obj.bar
, возвращает другую функцию (назовем ее анонимной функцией B), которая создана как стрелочная функция. В результате вызова функции A, this функции B замкнут на this,
принадлежащий obj.bar
(функции A). this
функции B, всегда будет иметь то значение, которое он получил при создании. В примере выше this функции B
указывает на this функции A, который указывает на
obj, таким образом this будет указывать на obj
даже когда будет вызван методом, который в нормальных условиях устанавливал значение this равным undefined или глобальному объекту
( или любым другим методом, как в предыдущих примерах).
В методе объекта
Когда функция вызывается как метод объекта, используемое в этой функции ключевое слово this
принимает значение объекта, по отношению к которому вызван метод.
В следующем примере, когда вызвано свойство o.f()
, внутри функции this
привязано к объекту o.
Необходимо отметить, что на поведение this совсем не влияет то, как или где была определена функция. В предыдущем примере мы определили функцию внутри свойства f
во время определения объекта o
. Однако мы могли бы также просто определить сначала функцию, а затем закрепить ее за за свойством o.f
. В этом случае поведение this не изменится:
Эти примеры показывают, что имеет значение только то, что функция была вызвана из свойства f
объекта o
.
Аналогично, привязывание this
обуславлавливается наличием ближайшей ссылки на объект или свойство. В следующем примере, когда мы вызываем функцию, мы обращаемся к ней как к методу g
объекта o.b
. На этот раз во время выполнения this,
что находится внутри функции, будет ссылаться на o.b
. Тот факт, что объект является членом объекта o
, не имеет значения; важна только ближайшая ссылка.
this
в цепочке object's prototype
this
в цепочке object's prototypeЭто же представление справедливо и для методов, определенных где-либо в цепочке object's prototype. Если метод находится в цепочке прототипов, то this ссылается на объект, на котором был вызван метод, т.е. так, словно метод является методом самого объекта, а не прототипа.
В этом примере, объект, которому присвоена переменная p,
не имеет собственного свойства f
, а наследует это свойство от своего прототипа. Однако совершенно неважно, что поиск свойства f в конце концов обнаружит его на объекте o
. Поскольку поиск начался с p.f
, то и свойство this
внутри функции f
будет ссылаться на объектp
. Таким образом, если f
вызывается как метод p
, то и this
относится к p
. Это полезная особенность прототипного наследования JS.
this
с геттерами/сеттерами
this
с геттерами/сеттерамиВсе те же утверждения справедливы, если функция вызывается из геттера или сеттера. Для функции, которая используется как геттер или сеттер, this
привязан к объекту, свойство которого необходимо извлечь через геттер/сеттер.
В конструкторе
Когда функция используется как конструктор (с ключевым словом new
), this
связано с создаваемым новым объектом.
Примечание: по умолчанию конструктор возвращает объект, к которому ссылается this
, но он может вернуть и другой объект (если возвращаемое значение не является объектом, тогда будет возвращен объект с this
).
В последнем примере (C2
) из-за того, что конструктор вернул объект, новый объект, к которому было привязано this,
был просто отброшен. (Это фактически делает выражение "this.a = 37;
" "мертвым" кодом. Он не является буквально нерабочим, так как он выполняется, но он может быть изъят без каких-либо внешних эффектов.)
call
иapply
call
иapply
Когда в теле функции используется ключевое слово this
, его значение может быть привязано к конкретному объекту в вызове при помощи методов call
илиapply
, которые наследуются всеми функциями от Function.prototype
.
Необходимо отметить, что если методам call
и apply
передается значение с this,
которое не является при этом объектом, будет предпринята попытка конвертировать значение в объект, используя внутреннюю операцию ToObject
. Если переданное значение является примитивным типом, таким как 7
или 'foo'
, оно будет преобразовано в объект с использованием родственного конструктора, так примитив 7
преобразовывается в объект через new Number(7),
а строка 'foo'
в объект через new String('foo')
и т.д.
Метод bind
bind
ECMAScript 5 ввел Function.prototype.bind
. Вызов f.bind(someObject)
создает новую функцию с тем же телом и областью видимости, что и f
, но там, где находится this
в исходной функции, в новой функции существует постоянная привязка к первому аргументу метода bind
несмотря на то, как используется данная функция.
Как обработчик событий DOM
Когда функция используется как обработчик событий, this
присваивается элементу, с которого начинается событие (некоторые браузеры не следуют этому соглашению для слушателей, добавленных динамически с помощью всех методов кромеaddEventListener
).
В инлайновом обработчике событий
Когда код вызван из инлайнового обработчика, this указывает на DOM-элемент, в котором расположен код события:
Код выше выведет 'button
'. Следует отметить, this будет указывать на DOM элемент только во внешних (не вложенных) функциях:
В этом случае this
вложеной функции не будет установлен, так что будет возвращен global/window объект.
Last updated