Демистификация стека вызовов JavaScript

JavaScript — это однопоточный язык с одним параллельным выполнением, что означает, что он может обрабатывать одну задачу за раз или часть кода за раз. Он имеет единый стек вызовов, который вместе с другими частями составляет модель параллелизма Javascript (реализованную внутри V8).

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

На самом базовом уровне стек вызовов — это структура данных, в которой используется принцип «последним пришел — первым обслужен» (LIFO) для хранения вызовов функций и управления ими.

Поскольку стек вызовов один, выполнение функций выполняется по одному сверху вниз, что делает стек вызовов синхронным. При управлении и хранении вызовов функций стек вызовов следует принципу «последний вошел, первый вышел» (LIFO), и это означает, что последнее выполнение функции, которое помещается в стек вызовов, всегда очищается в тот момент, когда стек вызовов очищается. выскочил.

Какой цели служит стек вызовов в приложении JavaScript? Как JavaScript использует эту функцию?

Когда движок JavaScript запускает ваш код, создается контекст выполнения, этот контекст выполнения является первым создаваемым контекстом выполнения и называется Global Execution Context. Первоначально этот Execution Context будет состоять из двух вещей — глобального объекта и переменной с именем this.

Теперь, когда функция выполняется в JavaScript (когда функция вызывается с () после метки), JavaScript создает новый контекст выполнения, называемый local execution context. Таким образом, для каждого выполнения функции создается новый контекст выполнения.

На всякий случай, если вам интересно, контекст выполнения — это просто среда, в которой выполняется код JavaScript. Контекст выполнения состоит из:

  • Нить исполнения и
  • Локальная память

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

образ стека вызовов

Что происходит, когда функция выполняется, и JavaScript создает контекст выполнения для выполнения этой функции. Вновь созданный контекст выполнения помещается в стек вызовов. Теперь все, что находится на вершине стека вызовов, находится там, где будет находиться поток JavaScript. Первоначально, когда JavaScript запускает приложение и создает global execution contextон помещает этот контекст в стек вызовов, и, поскольку он оказывается единственной записью в стеке вызовов, поток JavaScript живет в этом контексте и выполняет каждый найденный там код.

Теперь, когда функция выполняется, новая execution context создается, на этот раз localон помещается в стек вызовов, где занимает верхнюю позицию и автоматически, именно туда будет перемещаться поток JavaScript, выполняя найденные там инструкции.

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

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

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

function randomFunction() { function multiplyBy2(num) { return num * 2; } return multiplyBy2; } let generatedFunc = randomFunction(); let result = generatedFunc(2); console.log(result) //4

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

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

  1. global execution context создается и помещается в call stack.
  2. JavaScript создает пространство в памяти, чтобы сохранить определение функции и присвоить его метке. randomFunctionфункция просто определена, но не выполняется в данный момент.
  3. Следующий JavaScript, приходит к утверждению let generatedFunc = randomFunction() и поскольку он не выполнил функцию randomFunction() пока что, generatedFunc будет равняться undefined.
  4. Теперь, поскольку JavaScript столкнулся со скобками, которые означают, что функция должна быть выполнена. Он выполняет функцию, и мы помним, что при выполнении функции создается новый контекст выполнения, то же самое происходит и здесь. Новый контекст выполнения, который мы можем назвать randomFunc() создается и помещается в стек вызовов, занимая верхнюю позицию и отправляя глобальный контекст выполнения, который мы бы назвали global() ниже в стеке вызовов, заставляя поток JavaScript находиться в контексте randomFunc().
  5. Поскольку поток JavaScript находится внутри randomFunc()он начинает запускать найденные внутри коды.
  6. Он начинается с того, что просит JavaScript освободить место в памяти для определения функции, которое будет присвоено метке. multiplyBy2а так как функция multiplyBy2 еще не выполняется, он перейдет к оператору return.
  7. К тому времени, когда JavaScript встречает ключевое слово return, мы уже знаем, что произойдет, верно? JavaScript завершает выполнение этой функции, удаляет контекст выполнения, созданный для функции, и выталкивает стек вызовов, удаляя контекст выполнения функции из стека вызовов. Для нашей функции, когда JavaScript встречает оператор return, он возвращает любое значение, указанное для возврата к следующему контексту выполнения, и в этом случае это наш global() контекст выполнения.

В заявлении return multiplyBy2было бы хорошо отметить, что возвращается не метка multiplyBy2 но ценность multiplyBy2. Помните, мы попросили JavaScript создать место в памяти для хранения определения функции и присвоить его метке. multiplyBy2. Итак, когда мы возвращаемся, возвращается определение функции, и оно присваивается переменной generatedFuncизготовление generatedFunc что мы имеем ниже:

let generatedFunc = function(num) { return num * 2; };

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

В следующей строке let result = generatedFunc(2)мы выполняем определение функции, которое generatedFunc относится к (ранее нашему multiplyBy2), то происходит следующее:

  1. Переменный результат равен undefined так как в это время функция, на которую он ссылается, не была выполнена.
  2. JavaScript создает другой контекст выполнения, который мы бы назвали generatedFunc(). Когда создается локальный контекст выполнения, он состоит из локальной памяти.
  3. В локальной памяти мы бы присвоили аргумент 2 к параметру num.
  4. Не забываем, локальный контекст выполнения generatedFunc() будет помещен в стек вызовов, и, занимая верхнюю позицию, поток JavaScript будет запускать каждый код, найденный внутри него.
  5. Когда JavaScript встречает оператор return, он оценивает num * 2и с тех пор num относится к 2 изначально хранящийся в локальной памяти, он вычисляет выражение 2*2 и возвращает его.
  6. При возврате оценки выражения 2*2JavaScript завершает выполнение generatedFunc функция, возвращаемое значение сохраняется в переменной result затем стек вызовов выталкивается, удаляя generatedFunc() контекст и возвращение потока к global() контекст. Итак, когда мы console.log(result)мы получаем 4.

В заключение:

Ключевые вещи, которые нужно вынести из этой статьи, это то, что;

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

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

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

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *