Задумывались ли вы когда-нибудь, как интерпретатор управляет таким количеством вызовов функций в скрипте, например, когда мы рекурсивно решаем проблему или во время выполнения асинхронного javascript? Что ж, ответ на этот вопрос — Call Стек.

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

Стек вызовов: базовый

Стек — это линейная структура данных, которая следует определенному порядку выполнения операций. Порядок может быть следующим: LIFO (последним пришел — первым ушел) или FILO (первым пришел — последним ушел). В стеке первое вставленное значение может быть извлечено только, наконец, когда стек опустошен,это похоже на стопку книг, где одна книга кладется на другую, если вы хотите взять самая нижняя книга, сначала нужно удалить верхние

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

Стек вызовов: рабочий

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

function dummyName(){
   return length()*width();
}
function length() {
   return calcLength(5);
}
function calcLength(n){
   return n*n;
}
function width() {
   return 10;
}
console.log(dummyName());

Здесь у нас есть четыре функции: dumyName, length, calcLength и width, мы вызываем функцию dumyName, которая в конечном итоге вызывает остальные функции. Когда начнется выполнение кода, изначально стек вызовов будет пуст, так как интерпретатор будет разбирать код, при выполнении последней строки будет вызвана функция dumyName и одновременно она будет помещена в стек вызовов.

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

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

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

Функция dumyName все еще находится в стеке вызовов, и после выполнения функции длины выполнение кода вернется к оператору return функции dumyName. Выполнение оператора return функции dumyName происходит слева направо, поэтому сначала была вызвана функция длины, а теперь будет вызываться функция ширины и происходить процесс помещения функции в стек вызовов.

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

Обратите внимание:

  • Стек представляет собой структуру данных FILO (First In Last Out).
  • Функция остается в стеке вызовов до завершения выполнения.
  • Выполнение функции dumyName было слева направо, сначала вызывалась функция длины, а функция ширины не вызывалась до тех пор, пока не завершится выполнение функции длины, а затем результат обеих функций умножался до возвращение.
  • Когда текущая функция завершена, интерпретатор удаляет ее из стека и возобновляет выполнение с того места, где оно было остановлено в последнем листинге кода.
  • Если стек занимает больше места, чем было выделено, возникает ошибка "переполнение стека".

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