Многие разработчики писали код на JavaScript, чтобы сохранить его наследие с тех пор, как Брендан Эйх разработал JavaScript еще в 1995 году. Если кто-то действительно хочет изучать JavaScript, вам нужно понимать JavaScript. Вам нужно знать историю JavaScript, какие проблемы он на самом деле решает и как JavaScript работает за кулисами. Эта справочная информация поможет вам, когда вы будете программировать и изучать некоторые новые функции.

В этой статье мы немного познакомимся с историей JavaScript. Как работает JavaScript внутри? Что такое среда выполнения? Какую роль движок JavaScript играет в выполнении кода? Что такое Контекст выполнения? Что такое Scope, TDZ и многое другое?

Вы готовы 😉

История JavaScript

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

На заре Интернета (да, каменный век 😉) существовали способы создания веб-страниц (HTML был выпущен в 1993 году), но не было эффективного способа ими манипулировать. Так как мы используем DOM (объектная модель документа — будет обсуждаться позже) в эти дни.Поэтому эти статические страницы обычно не были интерактивными. Это было основной причиной, по которой необходимо было разработать какой-то язык, чтобы дать им жизнь.

Еще в 1995 Брендан Эйх выступил вперед и разработал первую версию JavaScript всего за 10 дней, ну, это не опечатка, я действительно имел в виду 10 дней. Тогда он назывался LiveScript (прикольное название, но JavaScript мне больше нравится 😉). В то время JavaScript был чем-то вроде инопланетной технологии и был быстро принят. Сообщество продолжало расширяться, оно становилось все лучше и лучше, и, конечно же, JavaScript, который мы используем, — это монстр по сравнению с теми днями.

Сначала он был разработан для Netscape 2 и стал стандартом ECMA-262 в 1997 году. В 1997 годубыл выпущен и поддерживалсяECMAScript 1. в Internet Explorer 4 впервые. Затем в 1998 г. вышел ECMAScript 2, а в 1999 г. — ECMAScript 3. Это четвертое издание языка немного задержалось и было выпущено в 2008 г., но не смогло выйти на рынок. Пятая основная версия, ECMAScript 5, была выпущена в 2009 году. Эта версия использовалась некоторое время, а последняя версия ECMAScript 6 была выпущена еще в 2015 году. >, который в настоящее время широко поддерживается во всех основных браузерах, за исключением Internet Explorer. Здесь вы можете больше узнать об истории любимого языка.

Здесь ECMAscript

ECMAScript — это стандарт JavaScript, предназначенный для обеспечения совместимости веб-страниц в разных веб-браузерах. Стандартизирован компанией Ecma International в соответствии с документом ECMA-262. По сути, он стандартизирует, что делает код JavaScript, поэтому JavaScript ведет себя одинаково в каждом браузере.

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

JavaScript на простом английском

  • JavaScript — это интерпретируемый язык, что означает, что, как и Java (нет сходства с JavaScript, за исключением названия), он не требует от нас компиляции перед отправкой в ​​браузер для выполнения, интерпретатор может принимать необработанный JS. исходный код и выполнить его для нас.
  • JavaScript является однопоточным и синхронным по своей природе. Это означает, что он создает и выполняет все задачи в одном потоке выполнения. Задачи ставятся в очередь одна за другой, и следующая задача должна сидеть и ждать, пока активная задача не будет завершена. Есть еще способы запуска JS-кода асинхронно, которые мы обсудим далее в этой статье.
  • JavaScript – это неблокирующий язык программирования или сценариев, который не блокирует предстоящие задачи, если программа выполняется так долго, благодаря веб-API, очереди обратного вызова и циклу обработки событий. На данный момент вы можете найти этот неблокирующий оператор противоречащим приведенному выше однопоточному оператору. Но мы обсудим все эти концепции в мельчайших подробностях через некоторое время. Тебе просто нужно быть со мной следующие несколько минут.
  • JavaScript — это динамически типизированный язык, что означает, что var может хранить данные любого типа, например int, string или object, в отличие от других статически типизированных языков, таких как C и C++, в которых мы должны явно указывать переменную. введите тип данных variable_name`.

Среда выполнения

Среда выполнения — это особая среда, которая предоставляет нашему коду доступ к встроенным библиотекам, API и объектам нашей программы, чтобы она могла взаимодействовать с внешним миром и выполняться для полного выполнения своей благородной цели.

Среда выполнения — это, по сути, фабрика, куда загружается необработанный код JavaScript, и он выполняется с помощью сочетания веб-API, библиотек и объектов. Надеюсь, это дало вам некоторое представление о среде выполнения. Если у вас все еще есть какие-то сомнения, не волнуйтесь, мы скоро обсудим компоненты среды выполнения и их роль в выполнении JS.

В контексте веб-браузера среда выполнения состоит из следующих компонентов:

  • Механизм JavaScript
    * Стек вызовов или стек выполнения
    * Куча
  • Веб-API (например, выборка, setTimeout, DOM, File API)
  • Очередь обратного вызова
  • Цикл событий

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

Современный браузер — это очень сложная программа с десятками миллионов строк кода. Таким образом, он разделен на множество модулей, которые обрабатывают различную логику.
Двумя наиболее важными частями веб-браузера являются движок JavaScript и движок рендеринга.

Механизм визуализации

Механизм рендеринга рисует содержимое на экране. Blink — это механизм рендеринга, отвечающий за весь конвейер рендеринга, включая деревья DOM, стили, события и интеграцию с V8. Он анализирует деревья DOM, разрешает стили и определяет визуальную геометрию элементов на экране.

JavaScript-движок

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

Механизм JavaScript компилирует и выполняет необработанный исходный код JavaScript в собственный машинный код. Таким образом, движок JavaScript — это место, где исходный код JS загружается, анализируется, интерпретируется и в конце выполняется в виде двоичного кода. Каждый крупный поставщик броуэров разработал свой собственный движок JavaScript, который в основном работает одинаково. Например, в Chrome используется V8, в Safari используется JavaScriptCore, а в Firefoxиспользуется SpiderMonkey.

В этой статье мы сосредоточимся на двигателе V8. Прочитайте, что такое V8 Engine со слов Google:

V8 — это высокопроизводительный движок JavaScript и WebAssembly от Google с открытым исходным кодом, написанный на C++. Он используется, в частности, в Chrome и в Node.js. Он реализует ECMAScript и WebAssembly и работает в системах Windows 7 или более поздних версий, macOS 10.12+ и Linux, использующих процессоры x64, IA-32, ARM или MIPS. V8 может работать автономно или может быть встроен в любое приложение C++.

Работа движка JavaScript (V8)

Первая версия V8 была выпущена и использовалась командой Google Chrome в 2010 году, когда у них возникли проблемы с отображением Google Maps. Позже они со временем улучшили его и выпустили еще одну версию V8 в 2017 году, которая в наши дни используется браузером Chrome.

Текущая версия была построена на этой модели;

[Изображение скопировано из этой замечательной статьи Uday Hiwarale]

Давайте разберем его;

Движок V8 загружает исходный код JS и передает его Baseline Compiler, который обрабатывает и компилирует код в облегченный ByteCode.

Этот байт-код передается интерпретатору, который в основном представляет собой алгоритм IntelliSense, который знает, что делает JS-код. Он интерпретирует байт-код для понятного процессору двоичного кода.

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

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

Это базовая модель функционирования Chrome JavaScript Engine (V8). Другие поставщики браузеров используют другие механизмы JavaScript. Но действуют они примерно одинаково.

Как код JavaScript работает в среде выполнения

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

Рассмотрим эту вечную петлю;

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

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

Благодаря современным браузерам они используют отдельные потоки выполнения JavaScript для разных вкладок, иначе наш браузер завис бы, если бы в одной вкладке на одной странице встретилась такая тяжелая программа или бесконечный цикл. Современные веб-браузеры обычно используют отдельные потоки выполнения для разных вкладок или один поток выполнения для одного домена (один и тот же веб-сайт на нескольких вкладках). Chrome использует политику «один процесс на сайт», поэтому, если несколько доменов были открыты на разных вкладках, все они перестанут работать. Вкладки других доменов (веб-сайтов) будут работать нормально.

Давайте подробно обсудим движок JavaScript. Движок JavaScript состоит в основном из двух компонентов; Стек вызовов и куча.

Куча

Куча — это неструктурированное хранилище памяти, где переменные и объекты сохраняются во время выполнения программы. Затем куча, также известная как куча памяти, очищается во время сборки мусора. Я не буду подробно рассказывать об этом, вы можете ознакомиться с этой замечательной статьей.

Стек вызовов или стек выполнения

Нас интересует стек вызовов, который в основном представляет собой хранилище данных LIFO (первым поступил, последним обслужен), где текущие контексты выполнения сохраняются во время работы программы. Чуть позже мы подробно обсудим, что такое Контекст выполнения. Каждая запись в стеке вызовов называется фреймом стека. Фрейм стека содержит информацию о контексте выполнения, такую ​​как объект аргумента, адрес возврата, локальные переменные и т. д.

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

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

Во время выполнения контекста выполнения определенный код анализируется синтаксическим анализатором, переменные и объявления сохраняются в памяти (VO), и генерируется исполняемый байт-код, который затем преобразуется в двоичный код, который выполняется.

Во время выполнения создаются два типа контекстов выполнения;

  • Глобальный контекст выполнения (GEC)
  • Функциональный контекст выполнения (FEC)

Глобальный контекст выполнения (GEC)

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

Это основной/по умолчанию контекст выполнения, который инкапсулирует все функциональные контексты выполнения.

Для любого файла сценария существует только один GEC.

Функциональный контекст выполнения (FEC)

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

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

Как создается контекст выполнения?

Как мы уже знаем, у контекста выполнения есть две основные задачи: он подготавливает сценарий, а затем выполняет его. Итак, мы попытаемся понять контекст выполнения в два этапа;

  • Этап создания
  • Фаза выполнения

Этап создания

Фаза создания любого контекста выполнения завершается тремя основными этапами.

  • Создание переменного объекта
  • Создание цепочки областей видимости
  • Присвоение этого значения

Фаза создания: создание переменного объекта (VO)

Для GEC создается переменный объект, который в основном представляет собой контейнер памяти, в котором хранятся свойства для всех переменных и объявлений функций, а также хранятся ссылки на них. Когда переменная встречается в глобальном контексте выполнения, свойство добавляется к объекту переменной и инициализируется (в случае определения с помощью var) со значением по умолчанию undefined.

Когда встречается объявление функции, к объекту Variable добавляется свойство, и ссылка на эту функцию сохраняется как значение.

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

В случае FEC (функциональный контекст выполнения) создается не ВО, а подобный массиву объект, называемый объектом аргумента. Все аргументы, полученные функцией, хранятся в этом массивоподобном объекте.

Подъем в JavaScript

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

Проще говоря, перед выполнением кода JavaScript хранит все переменные, функции и объявления классов в контейнере памяти, который называется Variable Object в верхней части области видимости. Этот процесс известен как Hoisting в JavaScript. По сути, подъем — это причина, по которой мы можем получить доступ к функциям и переменным даже до того, как они будут объявлены.

Функция подъема

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

Переменный подъем

Объявления переменных также поднимаются. Переменные, объявленные с ключевым словом var, поднимаются и инициализируются со значением по умолчанию undefined. Это означает, что если вы попытаетесь получить доступ к переменной, объявленной с помощью var, вы не получите ошибку, но значение будет неопределенным.

Если вы объявляете переменную с ключевым словом letили const, их объявление поднимается, но не инициализируется значением по умолчанию undefinedvalue. Вот почему вы получите uncaught ReferenceError , когда мы попытаемся получить к нему доступ до объявления.

Здесь действительно важно обсудить одну вещь. Вас могут спросить об этом в интервью.

Начиная с верхней части области до полной инициализации переменной, говорят, что эта переменная находится в временной мертвой зоне (TDZ). Вы всегда должны пытаться получить доступ к переменным за пределами своей TDZ. Если вы попытаетесь получить доступ к переменной из ее TDZ, вы получите ReferenceError. Здесь возникает вопрос, где начинается ТДЗ, а где заканчивается.

См. приведенный ниже код, вы будете знать, что Temporal Dead Zone начинается в начале блока кода (верхняя часть области) и заканчивается при инициализации. Переменные, объявленные с помощью let и const, следуют тому же шаблону.

В случае переменных, объявленных с помощью var, сценарий немного отличается. И виноватый парень тащит. Как мы уже знаем, переменные, объявленные var, поднимаются и инициализируются одновременно со значением по умолчанию undefined. И мы также знаем, что TDZ заканчивается, когда переменной присвоено значение.

По этой причине var ведет себя немного иначе, чем letи const. См. этот код;

Надеюсь, с TDZ вам все ясно. Вернемся к подъему.

Существует одно строгое правило подъема: подъем работает только для операторов, а не для выражений. Взгляните на этот код;

Это потому, что мы присваиваем функциональное выражение переменной в качестве значения. Как мы все знаем, переменные, объявленные с помощью ключевого слова let, поднимаются, но выбрасывают ReferenceError, если вызываются в Temporal Dead Zone. Это связано с тем, что их значение не инициализируется во время подъема, а TDZ завершается только после полной инициализации переменной.

Время викторины: у вас есть небольшой вопрос? Что было бы на выходе, если бы мы использовали var вместо того, чтобы вводить приведенный выше пример кода. Можете ли вы угадать, будет ли выброшена какая-либо ошибка или вывод функции будет записан в консоль?

Я уверен, что вы угадали это правильно. В этом случае будет выдана ошибка о том, что что-то вроде myFunc не является функцией, потому что на этом этапе значение переменной myFunc будет undefined из-за подъема. Таким образом, вызов undefined() вызовет ошибку.

Фаза создания: создание цепочки Scope

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

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

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

Всякий раз, когда где-то вызывается переменная или функция, движок начинает искать внутри локальной области видимости, где она была вызвана. Если не найдено, он ищет родительские области (лексическая область) одну за другой, от внутренней (локальной) до самой внешней (глобальная область). Если переменная не была найдена в локальной и во всех родительских областях, включая глобальную корневую область, тогда механизм выдает ошибку.

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

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

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

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

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

[Изображение скопировано из этой замечательной статьи Victor Ikechukwu на freeCodeCamp]

Как вы можете видеть на этом изображении, функция second имеет доступ ко всем областям, включая собственную область local, область действия функции first и область global . Но сначала функция имеет доступ только к своей локальной и глобальной области видимости. Вы можете видеть, что он не имеет доступа к области действия функции second.

На этом второй шаг создания контекста выполнения завершается.

До сих пор объект Variable был создан, цепочка областей видимости на месте. Давайте обсудим третий и последний шаг.

Этап создания: установка значения «this»

Следующим и последним шагом в создании контекста выполнения является установка значения this. this — это специальное ключевое слово в JavaScript, которое относится к области среды, к которой принадлежит контекст выполнения.

Значение this отличается в зависимости от контекста, в котором оно используется. Взгляните на эти случаи;

Случай 1: в глобальном контексте выполнения this относится к глобальному объекту окна в случае браузеров. Попробуйте ввести «это» в консоль внутри GEC, и вы увидите объект window. Здесь следует отметить одну интересную вещь. Когда вы определяете переменную или функцию внутри GEC, они сохраняются как свойство объекта окна. Это означает, что эти два утверждения эквивалентны;

И это выведет true на консоль;

Вариант 2: внутри FEC новый объект thisobject не создается, а вместо этого ссылается на контекст, к которому он принадлежит. Например, в этом случае this относится к глобальному объекту окна. Поскольку эта функция объявлена ​​в GEC, это будет ссылаться на глобальный объект window.

Вариант 3:в случае объектов использование this внутри методов относится не к глобальному объекту, а к самому объекту. Посмотрите на этот пример;

Случай 4: внутри функций-конструкторов this относится к вновь созданному объекту, когда вызывается с ключевым словом new, подобным этому;

На этом фаза создания Execution Context завершена. До сих пор все было сохранено в VO, цепочка областей действия создана, и значение этого находится на месте.

Этап выполнения

Сразу после создания контекста выполнения. Движок JavaScript запускает выполнение созданного контекста. Но переменный объект в настоящее время содержит все объявления этого конкретного контекста выполнения, но их значение равно undefined. И вы уже знаете, что мы не можем работать с undefined. Таким образом, движок JavaScript снова просматривает VO еще раз и передает их исходные значения. После того, как этот код анализируется механизмом синтаксического анализатора, он преобразуется в облегченный байт-код, который затем преобразуется в двоичный код (01), который затем выполняется.

Стек вызовов JavaScript (с точки зрения контекста выполнения)

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

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

Контекст выполнения сверху выполняется первым, и как только этот контекст выполняется (что-то возвращается), он извлекается из стека. Уже следующий контекст становится активным и начинается его выполнение. Этот процесс повторяется до тех пор, пока в стеке не останется последний GEC. Он выполняется в конце, и считается, что скрипт выполняется в этот момент.

Заключение

Подытожим все это на примере. Мы пытаемся вспомнить все, что мы узнали до сих пор в этой статье.

Рассмотрим эту программу внутри файла сценария;

Прежде всего, этот файл .js загружается в браузер и передается движку JavaScript для выполнения. Механизм создает глобальный контекст выполнения для этого файла, который обрабатывает выполнение корня этого файла сценария, всего, что не находится внутри функции. Этот GEC помещается внизу (или вверху) стека выполнения. Глобальный контекст выполнения создается в два этапа; этап создания и этап выполнения.

Переменная name=”Victor”хранится в объекте переменных (VO) GEC и инициализируется значением по умолчаниюundefined (подъем).

Затем для всех этих функций first, second и third в ВО GEC добавляется свойство, и ссылка на эти функции сохраняется как значение. Вы знаете, подъем 😉.

После установки VO GEC создается цепочка областей действия, и устанавливается значение this (window).

Теперь начинается выполнение. Но значение переменной name по-прежнему не определено в ВО. И мы не можем работать с undefined, верно? Итак, еще раз JS-движок просматривает VO и передает исходное значение переменной name, которое равно Victor. Теперь мы готовы продолжить нашу программу.

Прежде всего вызывается функция first. Механизм JS создает функциональный контекст выполнения для обработки его выполнения. Этот FEC размещается поверх глобального контекста выполнения, образуя стек вызовов или стек выполнения. В течение некоторого времени этот FEC является активным, поскольку мы уже знаем, что контекст выполнения вверху в стеке вызовов активен. Переменная a = "Hi!" сохраняется в FEC, а не в GEC.

В следующем операторе функция first вызывает функцию second. Другой FEC создается и размещается поверх FEC функции first. Теперь этот FEC активен. Переменная b= “Hey!” сохраняется в FEC.

Затем функция second вызывает функцию third, аналогичным образом создается FEC и помещается на вершину стека выполнения. Переменная c = “Hello!”сохраняется в FEC.

Пока стек выполнения выглядит так;

И записывает Hello Victor в консоль. Но ждать! Откуда родом Виктор? Переменная name не определена в функции third. Вы правильно догадались, Scope Chain. Функция third ищет переменную имени внутри своей локальной области видимости, но не находит. Из-за того, что называется лексической областью действия, он также имеет доступ к своей родительской области, глобальной области. Механизм JavaScript ищет name в глобальной области видимости и находит его.

Когда функция third выполняет все свои задачи, ее FEC выталкивается из стека выполнения (стека вызовов).

Тогда самый первый FEC под ним становится активным контекстом и начинает выполнение. Записывает Hey! Victor в консоль и выталкивает из стека выполнения. Теперь последний FEC этой программы становится активным и выводит Hi! Victor на консоль. После выполнения всех операторов он уничтожается и выталкивается из стека вызовов.

У нас снова остался только GEC в стеке исполнения. Кроме того, ему больше нечего выполнять, он также выскакивает из стека.

Надеюсь, этот пример развеял большинство ваших сомнений. До сих пор мы обсуждали первый компонент среды выполнения JavaScript. Надеюсь, вы найдете это полезным.

До сих пор мы узнали

  • Краткая история JavaScript
  • JavaScript Runtime, которая представляет собой специальную среду, предоставляемую браузером или контекстом, в котором мы запускаем наш код. Эта среда предоставляет нам объекты, API и другие компоненты, чтобы наш код мог взаимодействовать с внешним миром и быть казненным.
  • Компоненты среды выполнения; например, движок JavaScript, веб-API, очередь обратного вызова и цикл обработки событий.
  • движок JavaScript, который снова состоит из стека вызовов или стека выполнения и кучи.
  • Контекст выполнения, представляющий собой специальную среду для выполнения кода JavaScript, содержит код, который выполняется в данный момент, и все, что помогает в его выполнении. Эта специальная среда называется контекстом выполнения.
  • Типы контекста выполнения; например, глобальный контекст выполнения (GEC) и функциональный контекст выполнения (FEC).
  • Фаза создания контекста выполнения, состоящая из трех этапов; Создание VO, построение цепочки Scope и значение параметра this.
  • Этап выполнения Execution Context, мы узнали, как создается GEC после загрузки скрипта и как каждая функция создает свой собственный FEC. Они продолжают накладываться друг на друга, пока не вернут что-то и не выскочат из стека.
  • Область видимости и временная мертвая зона, мы узнали, как функции могут получать доступ к объявлениям из своей родительской области через лексическую область видимости. Мы также кратко обсудили TDZ.

Недостаток однопоточного характера JS

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

Но что, если текущая выполняемая задача занимает так много времени, что, если наш текущий контекст выполнения запрашивает некоторые данные с сервера (медленный сервер), определенно это займет некоторое время. В этом случае очередь стека вызовов будет зависать, так как JavaScript выполняет только одну задачу за раз. Все контексты выполнения, которые будут выполняться следующими, будут продолжать ждать, пока наш текущий виновный контекст выполнения не будет разрешен. Как мы можем справиться с таким поведением. Как мы можем запланировать некоторые задачи или припарковать некоторые дорогостоящие задачи, чтобы мы могли поддерживать работу нашего приложения? Как сделать наш синхронныйJavaScript асинхронным?

Здесь на помощь приходят веб-API, очередь обратного вызова и цикл событий. Я планировал обсудить все эти концепции в одном руководстве, но объем оказался больше, чем я ожидал.

Что дальше?

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

Вот часть 2 этого руководства, которое вы, возможно, захотите проверить. Я рассмотрел остальные компоненты среды выполнения JavaScript.

  • Веб-API
  • Очередь обратного звонка
  • Цикл событий

От 0 до 1 — Как работает JavaScript внутри (Часть 2)

Кредиты и мотивации

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

Заключительные слова

Если вам понравилось это руководство и вы хотите получать уведомления о моих следующих статьях, подобных этой, подписывайтесь на меня. Если ваш друг борется с этими понятиями, поделитесь этим руководством.

А пока оставайтесь в безопасности и старайтесь обезопасить других.

До скорой встречи💓