Наверное многие при изучении javascript сталкиваются с проблемой понимания. Что ж сейчас я попытаюсь вам объяснить механизм работы ключевого слова this.
Для начала давайте рассмотрим алгоритм исполнения ключевого слова this в спецификации.
Итак смотрим на производство PrimaryExpression: this и видим следующие шаги:
1. Return ? ResolveThisBinding().И это все. Но было бы не честно не раскрыть механизм этого шага, поэтому продолжим...
Теперь смотрим алгоритм ResolveThisBinding и наблюдаем такие этапы:
1. Let envRec be GetThisEnvironment().
2. Return ? envRec.GetThisBinding().Первый этап говорит нам о том что нужно получить Environment Record, при помощи вызова операции GetThisEnvironment.
Второй этап вызывает метод GetThisBinding у полученного Environment Record
Теперь давайте рассмотрим эти шаги подробнее. Итак открываем для начала алгоритм GetThisEnvironment:
1. Let env be the running execution context's LexicalEnvironment.
2. Repeat,
a. Let exists be env.HasThisBinding().
b. If exists is true, return env.
c. Let outer be env.[[OuterEnv]].
d. Assert: outer is not null.
e. Set env to outer.Пояснение: В первом шаге достается из Execution Context содержимое компоненты LexicalEnvironment и записывается в переменную env; содержимым является Environment Record. Второй шаг это бесконечный цикл с условием выхода в теле. Шаг 2.a сохраняет результат операции HasThisBinding (смотри примечание под знаком *) в переменную exists, смысл этой операции проверить Environment Record на способность устанавливать привязку this. Шаг 2.b смотрит на результат предыдущего шага то есть на переменную exists и выходит из цикла если результат true. Шаг 2.c достает значение из поля [[OuterEnv]] записи Environment Record и сохраняет в переменную outer. В шаге 2.d утверждаем что outer не может иметь значение null. Ну и 2.e это перезапись переменной env значением переменной outer.
В конечном итоге когда произойдет шаг 2.b мы получим результат этой операции GetThisEnvironment. Результат операции - получение Environment Record, которая поддерживает привязку this
Ну, а теперь осталось рассмотреть операцию/метод Environment Record - GetThisBinding. Но есть одна проблема: смотри примечание под знаком *. И поэтому я предлагаю посмотреть на реализацию одного из типов, пусть это будет function Environment Record. Реализация метода GetThisBinding такая:
1. Assert: envRec.[[ThisBindingStatus]] is not lexical.
2. If envRec.[[ThisBindingStatus]] is uninitialized, throw a ReferenceError exception.
3. Return envRec.[[ThisValue]].Внимание, выполнение этого алгоритма недостижимо если функция является стрелочной, так как в предыдущем алгоритме GetThisEnvironment, а именно на этапе выполнения шага 2.a мы получим false; стрелочные функции не имеют способности устанавливать привязку this
Пояснение: Первым шагом утверждаем, что поле [[ThisBindingStatus]] нашего Environment Record не имеет значение lexical (абзац выше как раз подтверждает этот шаг). Второй шаг проверяет поле [[ThisBindingStatus]] на значение uninitialized, если это не так то выбрасывается ошибка (кстати говоря эта ошибка возникает в том случае когда this не привязан к Environment Record, которое поддерживает привязку this; например когда мы делаем наследование класса от другого, но создавая метод "constructor" не привязываем значение this, то есть не пишем обязательное super(...args)). Ну и третий шаг это получение значения из поля [[ThisValue]], после получения происходит возврат этого значения.
Вот это механизм того как при написании в коде ключевого слова this вы получаете значение.
И пару слов об [[ThisValue]], так как очень непонятно получается, что возникает это поле и в нем уже значение необходимое нам. Итак, это значение устанавливается в 2ух случаях, при вызове функции (шаг 9) и при вызове ключевого слова super(...args) (шаг 8). За установку отвечает операция BindThisValue, в этом алгоритме происходит установка поля [[ThisValue]] значением переданным в BindThisValue. Но обращаю ваше внимание, что дополнение про [[ThisValue]] работает в случае когда у нас function Environment Record, при других типах об объяснении про [[ThisValue]] - забудьте.
Как-то так, если есть вопросы - буду рад на них ответить.
Примечания:
* данная операция, а точнее метод имеет разную реализацию так как зависит от того на каком типе Environment Record применяется. Типы Environment Record описаны здесь. В зависимости от типа варьируется и итоговый результат.
@thereisnoneed0 Видел его комментарии на learn.javascript.ru, а так нет не знаком.