Skip to content

Instantly share code, notes, and snippets.

@dSalieri
Last active September 27, 2023 09:11
Show Gist options
  • Save dSalieri/ee8e169e62607eec7fb47b8308402047 to your computer and use it in GitHub Desktop.
Save dSalieri/ee8e169e62607eec7fb47b8308402047 to your computer and use it in GitHub Desktop.
Что такое ключевое слово this; как ключевое слово this получает значение.

Наверное многие при изучении 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
Copy link

знакомы ли вы с творчеством demi myrich? у вас прям под капотом весь механизм описан. Очень здорово!

@dSalieri
Copy link
Author

dSalieri commented Jul 29, 2023

@thereisnoneed0 Видел его комментарии на learn.javascript.ru, а так нет не знаком.

@integratorivan
Copy link

Спасибо большое за статью!

@dSalieri
Copy link
Author

@ivanvkt пожалуйста ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment