Стек викликів
На момент виклику функції, всередині її тіла можуть викликатися інші функції, а в них - інші тощо. JavaScript - однопотокова мова, тобто за одну одиницю часу може виконуватись лише одна інструкція. Це означає, що викликані функції, які не завершили своє виконання, повинні чекати виконання функцій, викликаних всередині них, для того, щоб продовжити свою роботу.
function fnA() {
console.log("Лог всередині функції fnA до виклику fnB");
fnB();
console.log("Лог всередині функції fnA після виклику fnB");
}
function fnB() {
console.log("Лог всередині функції fnB");
}
console.log("Лог до виклику fnA");
fnA();
console.log("Лог після виклику fnA");
// "Лог до виклику fnA"
// "Лог всередині функції fnA до виклику fnB"
// "Лог всередині функції fnB"
// "Лог всередині функції fnA після виклику fnB"
// "Лог після виклику fnA"
Потрібен механізм зберігання списку функцій, які були викликані, але ще не завершили своє виконання, і механізм керування порядком виконання цих функцій - саме за це відповідає стек викликів (call stack).
Стек
Стек - структура даних, яка працює за принципом LIFO (Last-In-First-Out), тобто останнім прийшов - першим пішов. Останнє, що додається у стек, буде видалено з нього першим, - тому можна додати або видалити елементи тільки з верхівки стека.
Уявіть стек у вигляді масиву, у якого є тільки методи pop
і push
, тобто можна додати або видалити тільки елемент в кінці колекції.
Стек викликів
Стек викликів (call stack) - це механізм для відстеження поточного місцезнаходження інтерпретатора в коді, який викликає декілька функцій. Яка із функцій виконується на цей момент, які функції викликаються всередині функції, що виконується, яка функція буде викликана наступною тощо.
- Коли скрипт викликає функцію, інтерпретатор додає її в стек викликів і починає виконання.
- Будь-які функції, викликані функцією, що виконується, додаються у стек викликів і виконуються, щойно відбувається їх виклик.
- Коли виконання функції завершено, інтерпретатор знімає її зі стека викликів і відновлює виконання коду з тієї точки, де зупинився до цього. Тобто починає виконуватися функція, запис якої наступний у стеку.
Stack frame (кадр стека, запис стека) - структура, яка додається у стек на момент виклику функції. Зберігає службову інформацію, наприклад, ім'я функції і номер рядка, в якому був виклик.
function bar() {
console.log("bar");
}
function baz() {
console.log("baz");
}
function foo() {
console.log("foo");
bar();
baz();
}
foo();
Коли виконується цей код, спочатку викликається foo()
, потім всередині foo()
викликається bar()
, а потім - baz()
. Виклики console.log()
так само враховуються, адже - це функція. На ілюстрації нижче, для прикладу, покроково зображений стек викликів.
Переповнення стека викликів
Стек викликів - не безмежний, йому відводиться кінцевий об'єм пам'яті. Іноді в консолі можна побачити помилку "Uncaught RangeError: Maximum call stack size exceeded"
- переповнення стека (stack overflow).
Це може статися у випадку неправильного використання рекурсії або зациклення викликів функцій, тобто, якщо відбуваються нескінченні виклики функцій і результат не повертається, - стек збільшується. Така помилка виникне після досягнення ліміту кількості записів стека, і скрипт «падає».