Исходный адрес: https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf
PS: Давно ничего не писал.В последнее время готовлюсь к написанию своего блога.Наконец-то некоторые технические направления были доработаны,и я могу снова с удовольствием учиться.Серия узлов начнётся позже.
По мере того, как JavaScript становится все более и более популярным, все больше и больше команд широко применяют JavaScript для интерфейсных, серверных, гибридных приложений, встроенных и других областей.
Цель этой статьи — глубже изучить JavaScript и объяснить, как работает JavaScript. Понимая его базовую конструкцию и то, как он работает, мы можем помочь нам писать лучший код и приложения. Согласно статистике GitHub, JavaScript уже давно занимает GitHub.Active Repositories
а такжеTotal Pushes
возглавляет список и не сильно отстает в других категориях.
Если проект все больше полагается на JavaScript, это означает, что разработчики должны использовать эти языки и экосистемы для предоставления более глубокого основного контента для создания захватывающего приложения. Однако оказывается, что есть много разработчиков, которые используют JavaScript каждый день, не зная, как работает JavaScript внутри.
Обзор
Почти все слышали о концепции движка V8, и большинство людей знают, что JavaScript является однопоточным или что он использует очереди обратного вызова.
В этой статье мы подробно познакомим вас с этими понятиями и объясним, как работает JavaScript. Поняв эти детали, вы сможете использовать предоставленные API для написания лучших неблокирующих приложений. Если вы новичок в JavaScript, эта статья поможет вам понять, почему JavaScript такой «странный» по сравнению с другими языками. Если вы опытный разработчик JavaScript, надеюсь, это даст вам новое представление о среде выполнения JavaScript, даже если вы, вероятно, используете ее каждый день.
JavaScript-движок
Самым популярным движком JavaScript, конечно же, является движок Google V8. Движок V8 используется в Chrome и Node. Вот простая диаграмма, иллюстрирующая их взаимосвязь:
Двигатель в основном состоит из двух частей:
- Куча памяти: здесь происходит выделение памяти.
- стек вызовов: здесь выполняется ваш код
Время выполнения
Часто используются некоторые API-интерфейсы браузера (например, setTimeout), но эти API не предоставляются движком. Итак, откуда они взялись? На самом деле реальная ситуация здесь немного сложнее.
Таким образом, у нас есть множество API-интерфейсов вне движка, которые мы называем веб-API, предоставляемыми браузером, например DOM, AJAX, setTimeout и так далее.
А еще у нас есть циклы событий и очереди обратного вызова, которые так популярны.
стек вызовов
JavaScript — это однопоточный язык, что означает, что он имеет только один стек вызовов, поэтому он может выполнять только одну операцию за раз.
Стек вызовов — это структура данных, которая отслеживает, где мы находимся в программе. Если мы запустим функцию, она поместит ее на вершину стека. При возврате из этой функции функция извлекается из вершины стека, что и делает стек вызовов.
Рассмотрим следующий пример:
function multiply(x, y) {
return x * y;
}
function printSquare(x) {
var s = multiply(x, x);
console.log(s);
}
printSquare(5);
Когда программа начинает выполняться, стек вызовов пуст, затем выполняются следующие шаги:
Каждая запись в стеке вызовов называется __фреймом вызова__.
Это проясняет, как была построена трассировка стека и в каком состоянии был стек, когда возникло исключение. Давайте посмотрим на код ниже:
function foo() {
throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
foo();
}
function start() {
bar();
}
start();
Если это произойдет в Chrome (при условии, что этот код находится в файле с именем foo.js), будет сгенерирована следующая трассировка стека:
"переполнение стека", что происходит, когда вы достигаете максимального размера стека вызовов, и это довольно легко сделать, особенно если вы пишете рекурсию без полного ее тестирования. Давайте взглянем на следующий код:
function foo() {
foo();
}
foo();
Когда наш движок начинает выполнять этот код, он начинается с функции foo. Тогда это рекурсивная функция, и она начинает вызывать себя без каких-либо условий завершения. Таким образом, каждый шаг выполнения снова и снова добавляет одну и ту же функцию в стек вызовов. Тогда это выглядит так:
Затем в какой-то момент количество вызовов функций в стеке вызовов превышает реальный размер стека вызовов, и браузер решает его убить, выдавая ошибку, которая выглядит примерно так:
Выполнять код в одном потоке легко, потому что вам не нужно иметь дело со сложными сценариями, возникающими в многопоточной среде, такими как взаимоблокировки. Но работа в одном потоке также очень ограничивает. Поскольку JavaScript имеет только один стек вызовов, что произойдет, если фрагмент кода будет работать медленнее?
Параллелизм и цикл событий
Обработка вызовов функций в стеке вызовов занимает много времени, так что же происходит?Например, допустим, вы хотите выполнить сложное перекодирование изображения с помощью JavaScript в браузере.
Вы спросите? Что это за проблема? На самом деле проблема в том, что когда в стеке вызовов есть функции для выполнения, браузер ничего не может сделать, он заблокирован. Это означает, что браузер не может отображать, не может запускать другой код, он завис. Это может быть проблемой, если вы хотите иметь плавный пользовательский интерфейс в своем приложении.
И это не единственная проблема, как только ваш браузер начинает обрабатывать многочисленные задачи в стеке вызовов, он может довольно долго не отвечать на запросы. Большинство браузеров делают это, сообщая об ошибке, спрашивающей, хотите ли вы закрыть веб-страницу.
Таким образом, это не лучший пользовательский опыт, не так ли?
Итак, как выполнять сложный код, не блокируя UI, чтобы браузер не зависал?Решение — асинхронные обратные вызовы. Это подробно объясняется во второй части руководства «Как работает JavaScript»: «В движке V8: как писать оптимизированный код».