This
Ключевое слово this - это идентификатор (часто его называют контекстом вызова), значение которого устанавливается при вызове функции, если программист не переопределил правила через bind, то this устанавливается в зависимости от того каким образом функция была вызвана. Т. е. когда мы пользуемся ключевым словом this, мы, на самом деле, обращаемся с его помощью к некоторому объекту.
Определение на MDN - cвойство контекста выполнения кода (global, function или eval), которое в нестрогом режиме всегда является ссылкой на объект, а в строгом режиме может иметь любое значение.
Определение на learnjs– это объект «перед точкой», который используется для вызова метода. Его используют для доступа к информации внутри объекта.
Функции в JS, в отличие от других популярных языков, являются так называемыми объектами первого класса, то есть функция — это объект типа Function. Они существуют и имеют смысл сами по себе, без привязки к объекту.
В JavaScript ключевое слово this можно использовать в любой функции, даже если это не метод объекта. Оно вычиляется во время выполнения кода, в зависимости от контекста.
Обычно, если внутри функции используется this, тогда она ожидает, что будет вызвана в контексте какого-либо объекта. В глобальном контексте выполнения (за пределами каких-либо функций) this ссылается на глобальный объект вне зависимости от режима (строгий или нестрогий). Если вызвать функцию, возвращающую this, без объекта, то в строгом режиме получим this = undefined. В нестрогом режиме - глобальный объект window:
Стрелочные функции особенные: у них нет своего «собственного» this. Если мы ссылаемся на this внутри такой функции, то оно берётся из внешней «нормальной» функции.
Если функция вызывалась в MemberExpression
То есть в dot нотации, то this будет установлен в base идентификатора при вызове. this — это объект, которому принадлежит метод (функция, хранящаяся в объекте):
Тут this будет установлен в значение base - то есть в нашем случае obj. Подчеркиваю - установлен в момент вызова функции, а не в момент ее создания!
Другой пример:
Тут this будет указывать на secondObj!
Если продолжить код сверху, то в нестрогом режиме this будет указывать на globalObj, т.е. в браузере - window!
Также стоит отменить, что для обычных функций this будет равен тому объекту, который указан перед самой правой точкой. Пример:
При этом сама функция может быть объявлена вне объекта (в нашем случае в глобальной области видимости):
Функция в методе объекта: this во внутренней функции
Обычной ошибкой является уверенность в том, что this во внутренней функции такой же, как и во внешней. Вызов cpu3.getName() - это вызов метода, но вызов функции showName() — это уже вызов функции и поэтому её this — это глобальный объект window или undefined в strict mode. Даже если контекстом внешней функции getName является объект cpu3, внутри метода у него нет власти.
Чтобы решить проблему функция showName должна быть исполнена в том же контексте, что и метод getName. Это можно сделать при помощи неявного вызова, использую метод .call() (или .apply() или вообще сделать привязку через bind()):
Чтобы получить ожидаемый this, модифицируйте контекст внутренней функции при помощи непрямого вызова (используя .call() или .apply(), об этом позже) или создайте связанную функцию (используя .bind(), об этом тоже поговорим позже).
Если функция вызывается как CallExpression
то this устанавливается:
- в случае legacy mode и with statement, то на withStatement ( запрещен в strict mode, иными словами про него можно забыть );
- в случае strict mode на globalObj;
В случае ArrowExpression
this будет всегда в том значении, в котором он определен для Lexical Enviroment нашей ArrowExpression. Или для любой другой родительской Lexical Enviroment. Если проще то - this будет таким, каким он был на момент создания стрелочной функции (захватывается из текущего контекста). Кроме того стрелочную функцию нельзя использовать в качестве конструктора.
Функции-стрелки сохраняют значение this лексического окружения, в котором были созданы!
Вот наглядный пример (выполняем в браузере в нестрогом режиме):
При вызове стрелочной функции, this не устанавливается. Доступ к this осуществляется ровно так же, как и к любому другому идентификатору - по цепочке областей видимости, которая и является той самой цепочкой из Lexical Enviroment. Если происходит обращение к this, его значение берётся снаружи - как обычная переменная (из внешнего лексического окружения).
Пример, когда стрелочная функция вызывается внутри обычной, которая является методом объекта:
Тут this будет указывать на obj!
Продолжим пример:
this внутри arrow будет уже указывать на secondObj!
Если arrow() вынести из лексического окружения obj, т. е. сделать вот так:
То this всегда будет указывать на globalObj вне зависимости от того как будет вызываться method!
Если метод объекта сам является стрелочной функцией, то тут стоит обратить внимание на два случая. Первый - это когда объект объявлен при помощи фигурных скобок {...} (синтаксис литерала объекта). В таком случае, так как стрелочная функция не имеет своего this, то его значение возьмется по замыканию снаружи (как обычная переменная):
Второй случай, если объект создается через конструктор (в случае с классами разницы нет). В таком случае при использовании оператора new cоздаётся новый объект, наследующий Test.prototype. Вызывается конструктор — функция с указанными аргументами и this, привязанным к только что созданному объекту. Результатом выражения new становится объект, возвращённый конструктором. В таком случае при вызове метода, который объявлен, как стрелочная функция, this будет указывать на новый объект:
"Потеря контекста" (addEventListener, setTimeout)
Когда мы навешиваем на элемент обработчик события, то при его срабатывании this указывает на элемент, с которого началось событие.
При передаче в setTimeout и setInterval обычной функции значение this устанавливается в globalObj (наверно так устроено WEB API), оно не будет совпадать со значением this для функции, которая вызвала setTimeout. Для решения проблемы с setTimeout и setInterval можно использовать стрелочные функции. Наглядный пример:
Непрямой вызов: методы .call() и .apply()
Непрямой вызов производится, когда функция вызывается методами .call() или .apply().
Для того, чтобы при вызове функции установить this в определённое значение, используются методы call() или apply(). В данных методах первый параметр - это объект для использования в качестве this, последующие параметры для метода call - это аргументы функции, перечисляемые через запятую, а для метода apply - это массив агументов.
Привязка функции: метод .bind()
Спецификация ECMAScript 5 представила Function.prototype.bind(). Вызов f.bind(someObject) создаёт новую функцию с тем же телом и областью действия, что и f, но там, где в исходной функции используется this, в новой функции оно постоянно будет связано с первым аргументом bind, независимо от того, как функция используется.
Задачей метода .bind() является создание новой функции с контекстом, заданным в первом аргументе .bind(). Это — мощный инструмент, позволяющий создавать функции с заранее определённым значением this.
Если аргумент this передаётся в call, bind или apply при вызове стрелочной функции, он будет проигнорирован. После создания функции-стрелки значение this нельзя поменять указанными выше способами. Всё ещё возможно добавить аргументы к вызову, но первый аргумент (thisArg) должен быть установлен в null.
Краткий итог по определнию this
1.Так как важен вызов функции - Как функция вызывается?
2.Если функция стрелка - Какой this там, где объявлена стрелочная функция?
Методы массивов и this
Некоторые встроенные функции для объекта Array позволяют напрямую указать значение this для передаваемого коллбэка:
- Array.every
- Array.filter
- Array.find
- Array.findIndex
- Array.forEach
- Array.map
- Array.some
Школа web-программирования Constcode - youtube.com
ProCode IT School - youtube.com
Михаил Непомнящий - youtube.com
Hillel IT School - youtube.com
О ключевом слове «this» языка JavaScript* - tproger.ru