Как работает JavaScript [Объясняю визуально]

Kate

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

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

79ecd5db695cab72ae063db9561a593b.gif

Контекст выполнения​

«Все в JavaScript происходит внутри контекста выполнения (Execution Context)»

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

В этом контейнере есть два компонента: 1. Компонент памяти. 2. Компонент кода.

Компонент памяти также известен как переменная среды. В этом компоненте памяти переменные и функции хранятся в виде пар ключ-значение.

Компонент кода - это место в контейнере, где код выполняется по одной строке за раз. У этого компонента кода тоже есть необычное название, а именно «Поток выполнения» (Thread of Execution).

80558cb04c10b66e75f11d3bb28103a6.png

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

Выполнение кода​

Возьмем простой пример:

var a = 2;
var b = 4;

var sum = a + b;

console.log(sum);
В этом простом примере мы инициализируем две переменные, a и b, и сохраняем 2 и 4 соответственно. Затем мы складываем значение a и b и сохраняем его в переменной суммы.

Посмотрим, как JavaScript выполнит код в браузере.

858347da9cb447943f651e8a2de72490.gif

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

Браузер выполнит код JavaScript в два этапа.

  1. Фаза выделения памяти
  2. Этап выполнения кода
На этапе выделения памяти JavaScript сканирует весь код и выделяет память для всех переменных и функций в коде. Для переменных JavaScript будет хранить undefined на этапе выделения памяти, а для функций он сохранит весь код функции, который мы рассмотрим в следующем примере.

8e965c6b2504a915184d2fa9f0db99aa.gif

Теперь, на 2-м этапе, то есть при выполнении кода, он начинает проходить весь код построчно. Когда он встречает var a = 2, он присваивает значение 2 переменной 'a'. До сих пор значение «а» не было определено.

То же самое и с переменной b. Он присваивает 4 переменной «b». Затем он вычисляет и сохраняет значение суммы в памяти, равное 6. Теперь, на последнем шаге, он выводит значение суммы в консоль, а затем уничтожает глобальный контекст выполнения по мере завершения нашего кода.

Как вызываются функции в контексте выполнения?​

Функции в JavaScript, если сравнивать их с другими языками программирования, работают по-другому.

Возьмем простой пример:

var n = 2;

function square(num) {
var ans = num * num;
return ans;
}

var square2 = square(n);
var square4 = square(4);
В приведенном выше примере есть функция, которая принимает в качестве аргумента число и возвращает его квадрат.

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

Что касается функций, он сохранит всю функцию в памяти.

fa444552c4cdbbfc714a56623f04fe77.gif

А вот и самое интересное: когда JavaScript запускает функции, он создает контекст выполнения внутри глобального контекста выполнения.

Когда он встречает var a = 2, он присваивает значение 2 переменной 'n'. Строка номер 2 - это функция, и поскольку функции была выделена память ранее, все сразу перейдет к строке номер 6.

Переменная square2 вызовет функцию square, а javascript создаст новый контекст выполнения.

91656bef692c7da5147343056bf74e3c.gif

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

446c076b8184ce983d04d90556ce50b8.gif

После выделения памяти всем переменным внутри функции код будет выполняться построчно. Будет получено значение num, равное 2 для первой переменной, а затем вычислено ans. После вычисления ans возвратится значение, которое будет присвоено square2.

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

7ce641814459e1610b7b230f67ce6e18.gif

Теперь он будет следовать аналогичной процедуре для строки номер 7 или переменной square4, как показано ниже.

7c402e1479bf2af42d2b3f61f496e130.gif

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

Стек вызовов​

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

402138a19e4061430c85c8bd5808429e.png

JavaScript управляет созданием и удалением контекста выполнения кода с помощью стека вызовов. Стек - это упорядоченный набор элементов, в котором добавление новых элементов и удаление существующих элементов всегда происходит "с одной стороны". Первый элемент, добавленный в стек, будет удален оттуда последним. Этот принцип называется FILO.

Стек вызовов - это механизм, позволяющий отслеживать свое место в скрипте, вызывающем несколько функций.

Пример:

function a() {
function insideA() {
return true;
}
insideA();
}
a();
Мы создаем функцию «a», которая вызывает другую функцию «insideA», которая возвращает значение true. Я знаю, что код бессмысленный и ничего не делает, но он поможет нам понять, как JavaScript обрабатывает коллбеки (функции обратного вызова).

d3c30a92fc561d74a9329840bb5178e6.gif

JavaScript создаст глобальный контекст выполнения. Глобальный контекст выполнения выделит память для функции 'a' и вызовет 'function a' на этапе выполнения кода. Контекст выполнения создается для функции a, которая размещается над глобальным контекстом выполнения в стеке вызовов.

Функция a назначит память и вызовет функцию insideA. Контекст выполнения создается для функции insideA и помещается над стеком вызовов 'function a'. Теперь эта функция insideA вернет true и будет удалена из стека вызовов. Поскольку внутри 'function a' нет кода, контекст выполнения будет удален из стека вызовов.

 
Сверху