Движок JavaScript не похож ни на один другой движок. Его функции и возможности выходят далеко за рамки того, что мы собираемся здесь рассмотреть сегодня, однако мы стремимся выделить и обсудить некоторые внутренние механизмы контекста выполнения и ключевые процессы, которые имеют место.

Это обзор, поэтому ссылки и дополнительная литература доступны после основной части статьи, чтобы вы могли получить дополнительный контекст по обсуждаемым нами вопросам.

Итак, каков контекст выполнения?

Контекст выполнения подобен оболочке для кода, который в настоящее время выполняется в стеке выполнения.

Здесь мы обсудим два типа контекста выполнения.

  1. Глобальный контекст выполнения.
  2. Контекст выполнения функции.

Первый процесс, который происходит, - это создание глобального контекста выполнения.

В глобальном контексте выполнения есть два основных этапа.

  1. Фаза творения
  2. Фаза исполнения

Фаза творения

На этапе создания движок JavaScript анализирует наш код сверху вниз. Именно во время этого первого прохода движок JavaScript выполнит следующие ключевые шаги.

  1. Это создаст глобальный объект.
  2. Ключевое слово «this» определено.
  3. Создается ссылка на Внешнюю среду.
  4. Создает переменный объект.
  5. Подъем.

Мы видим это в следующем фрагменте кода. У нас есть редактор, который не содержит кода, однако, если мы запустим его, мы увидим, что мы создаем глобальный объект (окно) и ссылку на ключевое слово «this».

Как видите, ссылка «this» равна глобальному объекту, когда код выполняется вне функции.

Так что же такое глобальный объект?

В JavaScript глобальный объект будет зависеть от того, где выполняется ваш код. В браузере это будет «объект окна», тогда как во время выполнения узла он будет называться глобальным объектом.

Глобальный объект - это объект, который всегда существует в глобальной области ¹. Любая переменная или функция, объявленная вне оболочки функции, по определению является глобальной.

Так что же такое ключевое слово «это»?

В большинстве случаев значение this определяется способом вызова функции. Его нельзя установить путем присвоения во время выполнения, и он может отличаться при каждом вызове функции.

В JavaScript ключевое слово «this» - это просто ссылка на контекст выполнения функции во время выполнения или в глобальном контексте выполнения «this» будет глобальным объектом, таким как «Объект окна» в случае браузера.

Ссылка this назначается в зависимости от того, как и откуда была вызвана функция.

Ключевое слово это может и будет часто неожиданно меняться, однако обычно оно соответствует 4 правилам, которые мы выделим ниже. ²

Мы не будем вдаваться в подробности, однако эти правила следуют порядку приоритета, если несколько правил могут применяться к сайту вызова ³.

Приоритет 1. Жесткая привязка

Правило явной привязки с приоритетом 2 - вызовите или примените

Правило неявного связывания с приоритетом 3

Случай по умолчанию: обычные функции в глобальном масштабе.

Крайние случаи

Краевым случаем для этих правил будет «функция жирной стрелки».

У стрелочной функции нет собственного this. Используется значение this охватывающего лексического контекста.

Здесь у нас есть вложенная функция, которая ссылается на «это». Здесь мы не получаем того, чего ожидаем. Во вложенной функции мы теряем ссылку «this» на наш объект, а внутреннее вложенное «this» теперь ссылается не на объект, а на глобальный объект ??? Не то, что мы ожидали !!

Так как же нам на помощь функции жирных стрелок?

До стрелочных функций каждая новая определенная функция имела собственное значение this (в зависимости от того, как функция была вызвана). Это оказалось далеко не идеальным для объектно-ориентированного стиля программирования.

Стрелочная функция не имеет своего собственного «this», поэтому используется значение «this» включающего лексического контекста.

Он будет использовать цепочку областей видимости для определения значения «этого».

Выражения стрелочных функций лучше всего подходят для функций, не являющихся методами, в противном случае вы потеряете ссылку на значение «this» объекта.

Что такое переменный объект?

переменный объект - это контейнер данных, связанный с контекстом выполнения. Это специальный объект, в котором хранятся переменные и объявления функций, определенные в контексте.

В глобальном объекте это часто называется объектом глобальной контекстной переменной (VO). [Ссылка на видео 1.1 ниже]. Позже мы увидим аналогичный объект, созданный в контексте выполнения функции. В функции она будет называться функцией Activation Object (AO).

Что такое внешняя среда?

Движок JavaScript будет ссылаться на функции Lexical Environment. Это будет зависеть от того, где эта функция или переменная записана в коде. Он будет иметь дело с двумя областями действия.

  1. Глобальный масштаб
  2. Функция Scope.

Поскольку код анализируется на этапе создания, движок JavaScript будет ссылаться на все переменные за пределами этой области действия и будет определять, что функции могут получить доступ к этим переменным, если это потребуется, через цепочку областей видимости.

Именно здесь будет определено, где движок JavaScript должен искать, если переменная недоступна в этой функции.

Он будет искать во внешней среде и перемещаться вниз по цепочке областей видимости, пока не будет найден или не достигнет конца своей внешней среды области видимости.

Чтобы понять, как функция может ссылаться на свою внешнюю среду, давайте посмотрим на нее со следующей точки зрения.

Когда функция объявляется в вашем коде, эта функция во время выполнения будет помещена в память. Во время этого процесса, как мы обсуждали ранее, создается переменная или объект активации, который содержит ссылки на переменные и свойства. Если объявление функции помещается в память, для этой функции в памяти будет создано свойство fn. [[Scope]], которое будет иметь указатель на своего родителя. объекты свойство __parent__. Это свойство fn. [[Scope]] - это путь к его родительской области видимости.

Давайте посмотрим на это с визуальной точки зрения.

Что такое подъем?

Подъем - это процесс, выполняемый движком JavaScript на этапе создания контекста выполнения. Это создает пространство памяти для переменных и функций и делает их доступными во время их выполнения, даже если на них ссылаются до их объявления. Это называется подъемом.

Переменные физически не перемещаются. Однако они действуют так, как будто переменные помещаются в верхнюю часть скрипта, поскольку их область видимости находится в верхней части с необъявленным значением Undefined.

Значение переменной не присваивается до этапа выполнения, на котором механизм JavaScript анализирует код построчно. На этом этапе все, что делает механизм, - это поиск LHS (слева) каждый раз, когда он видит объявление переменной или объявление функции. Однако важно отметить, что в случае объявления функции эта функция помещается в память и устанавливается указатель, чтобы ее можно было запустить.

Другими словами, поиск LHS выполняется, когда переменная появляется слева от операции присваивания, а поиск RHS выполняется, когда переменная появляется справа от операции присваивания.

Поэтому, если к переменной обращаются до того, как код проанализирует ее на этапе выполнения, он вернет undefined. Выражение функции также на этом этапе будет ссылаться только на undefined, а не на саму функцию, тогда как на объявление функции будет выполняться при вызове, поскольку в памяти есть указатель на функцию.

Итак, на этом этапе наша фаза глобального создания завершена. Теперь у нас есть глобальный объект.

Ключевое слово «this» указывает на глобальный объект (окно в браузере или глобальное в Node), и наша область внешней среды настроена. Мы также предприняли подъем наших переменных и функций, однако мы выполнили только поиск LHS, поэтому до этого момента не было присвоено никаких значений.

Так что же теперь происходит?

Теперь мы переходим ко второй фазе контекста выполнения, фазе выполнения.

Фаза исполнения

На этапе выполнения компилятор будет анализировать код сверху вниз строка за строкой. Он проверит каждую строку и выполнит поиск RHS (правая сторона) в своей текущей области для каждого назначения и присвоит значение, если оно найдено, объявлению переменной. Если значение не назначено, оно останется неопределенным. Эти назначения будут добавлены к глобальному контексту выполнения и объекту глобальной переменной. [См. Ссылку на изображение 1.4]

Вызов функций

Если вызов функции обнаружен, он выполнит поиск в RHS, чтобы узнать, является ли переменная с таким именем функцией и находится ли она в памяти. Каждый раз, когда вызывается функция, она создает новый контекст выполнения функции для этой функции и помещает его в стек вызовов.

Как только эта функция помещается в стек вызовов, создается контекст выполнения этой функции. Это следует за процессом, аналогичным тому, что мы видели при создании глобального объекта.

Снова две фазы.

  1. Фаза создания.
  2. Фаза исполнения.

На этапе создания мы увидим создание,

  1. Ключевое слово this определено.
  2. Создается ссылка на Внешнюю среду.
  3. Создание объекта активации.
  4. Подъем.

Важно отметить, что каждая функция создает свой собственный контекст выполнения.

У него будет ссылка на внешнюю среду и доступ к цепочке областей видимости переменных [см. Ссылку на изображение 1.5 и 1.6].

Объект активации создается так же, как мы видели ранее с объектом глобальной переменной. Существенная разница с объектами активации заключается в том, что они также хранят формальные параметры и некоторые объекты аргументов.

Создается внутренняя область видимости функции, и ее внутренняя переменная и объявления функций поднимаются внутри оболочки функции.

Во время фазы выполнения функции мы увидим,

Поиск RHS. Если присутствует, то объявлению переменной будет присвоено значение.

Если обнаружен вызов функции, механизм JavaScript поместит эту вызванную функцию в стек выполнения, и процесс будет повторяться снова [см. Ссылку на видео 1.2].

Вывод

Контекст выполнения состоит из двух этапов. Создание «контекста выполнения глобального объекта» и создание «контекста выполнения функции». Каждый процесс разбит на два этапа. Сначала «этап создания», а затем «этап».

Этап создания отвечает за создание ссылки this, создание ссылок на внешнюю среду, создание объекта переменной / активации, а также подъем. Поднятие выполнит поиск LHS и выделит пространство памяти для переменных. Значение на этом этапе будет Undefined, как выделено механизмом JavaScript. Поиск RHS (присвоение значения) не выполняется до фазы выполнения.

Фаза выполнения отвечает за выполнение кода. Это также включает присвоение значений переменным посредством поиска RHS. Если значение не присвоено, то движком JavaScript переменной будет присвоено значение undefined.

После анализа оператора return функция извлекается из стека выполнения. После завершения всех функций и выполнения всего кода в глобальном контексте выполнения стек вызовов завершится и будет очищен.

Пожалуйста, обратитесь к ссылкам, включенным в эту статью, для более подробного объяснения ключевых понятий, о которых идет речь. Список литературы также включен ниже, а также Рекомендуемые учебные материалы для получения дополнительной контекстной информации.

Контактная информация





Ссылки

[1] MDN - глобальный объект

[2], [3] Вы не знаете JavaScript, This и прототипы объектов, Глава 2, Кайл Симпсон, 2014.

[4] Веб-документы MDM, Стрелочные функции 2019

[5] Вы не знаете JavaScript, области действия и замыкания, Кайл Симпсон, 2014

[6] MDN этот JavaScript

[7] Вы-не-знаете-JS. Понимание области, Кайл Симпсон, 2014

[8] Ядро JavaScript, Дмитрий Сошников, 2010

Рекомендуемые учебные материалы

  1. Вы не знаете JavaScript. Полный бесплатный список онлайн-книг Кайли Симпсон по JavaScript.
  2. Филип Робертс, помогите, я застрял в петле событий.
  3. Марья Хёлття: Разбор JavaScript - лучше лениться, чем нетерпеливо? | JSConf EU 2017
  4. JavaScript: понимание странностей
  5. Джейк Арчибальд: В петле - JSConf.Asia 2018
  6. Франциска Хинкельманн: JavaScript-движки - как они вообще? | JSConf EU 2017