Стек вызовов

Стек — это структура данных, представляющая собой список элементов, организованных по принципу LIFO (от англ. last in — first out, «последним пришёл — первым вышел»).

Стек можно представить в виде детской пирамидки: чтобы извлечь второе сверху кольцо, нужно сначала снять самое верхнее.

Массив как стек

Уже знакомые Вам методы push и pop позволяют работать с массивом как со стеком:

var stack = [];
stack.push('Tom');
stack.push('Jane');
stack.push('Bill');
stack.pop();  // Bill
stack.pop();  // Jane
stack.pop();  // Tom

Какое отношение стек имеет к функциям?

«Лабиринт» вызовов

function a() { b() }
function b() { c() }
function c() { d() }
function d() { console.log('D') }

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

«Лабиринт» вызовов

Пусть некоторый вызов из предыдущего примера привел нас в функцию d.

function d() {
  console.log('D');
  // Выпонение завершено, куда возвращаемся?
}

Что следует выполнять после завершения функции? Для ответа на этот вопрос существует стек вызовов.

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

Стек является «клубком ниток», который помогает среде исполнения найти дорогу в «лабиринте вызовов».

Записи в стеке вызовов называют фреймами (от англ. frame — кадр).

Разбор вызова по шагам

Шаг 1

Вызываем a().

/*1*/ function a() { b() }
/*2*/ function b() { c() }
/*3*/ function c() { d() }
/*4*/ function d() { console.log('D') }
/*5*/
/*6*/ →a();

В стек записывается <main>:6 (адрес, откуда сделан вызов).

Шаг 2

Находимся в a:1, вызываем b().

/*1*/ function a() { →b() }
/*2*/ function b() { c() }
/*3*/ function c() { d() }
/*4*/ function d() { console.log('D') }
/*5*/
/*6*/ a();

Стек: <main>:6, a:1

Шаг 3

Находимся в b:2, вызываем c().

/*1*/ function a() { b() }
/*2*/ function b() { →c() }
/*3*/ function c() { d() }
/*4*/ function d() { console.log('D') }
/*5*/
/*6*/ a();

Стек: <main>:6, a:1, b:2

Шаг 4

Находимся в c:3, вызываем d().

/*1*/ function a() { b() }
/*2*/ function b() { c() }
/*3*/ function c() { →d() }
/*4*/ function d() { console.log('D') }
/*5*/
/*6*/ a();

Стек: <main>:6, a:1, b:2, c:3

Шаг 5

Находимся в d:4, вызываем console.log().

/*1*/ function a() { b() }
/*2*/ function b() { c() }
/*3*/ function c() { d() }
/*4*/ function d() { →console.log('D') }
/*5*/
/*6*/ a();

Стек: <main>:6, a:1, b:2, c:3, d:4

Шаг 6 — возврат

Вернулись из console.log в d:4.

/*1*/ function a() { b() }
/*2*/ function b() { c() }
/*3*/ function c() { d() }
/*4*/ function d() { console.log('D')  }
/*5*/
/*6*/ a();

Стек: <main>:6, a:1, b:2, c:3

Шаг 7 — возврат

Вернулись в c:3.

/*1*/ function a() { b() }
/*2*/ function b() { c() }
/*3*/ function c() { d()  }
/*4*/ function d() { console.log('D') }
/*5*/
/*6*/ a();

Стек: <main>:6, a:1, b:2

Шаг 8 — возврат

Вернулись в b:2.

/*1*/ function a() { b() }
/*2*/ function b() { c()  }
/*3*/ function c() { d() }
/*4*/ function d() { console.log('D') }
/*5*/
/*6*/ a();

Стек: <main>:6, a:1

Шаг 9 — возврат

Вернулись в a:1.

/*1*/ function a() { b()  }
/*2*/ function b() { c() }
/*3*/ function c() { d() }
/*4*/ function d() { console.log('D') }
/*5*/
/*6*/ a();

Стек: <main>:6

Шаг 10 — конец

Вернулись в главную программу.

/*1*/ function a() { b() }
/*2*/ function b() { c() }
/*3*/ function c() { d() }
/*4*/ function d() { console.log('D') }
/*5*/
/*6*/ a(); 

Стек пуст.

Трассировка

Трассировка

Используйте console.trace(), чтобы увидеть стек вызовов в определенный момент работы программы.

function a() { b() }
function b() { c() }
function c() { d() }
function d() { console.trace() }

Используйте трассировку для отладки Ваших программ, но с осторожностью.

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

Задания для самоподготовки

  • Расскажите о принципе работы стека.
  • Напишите функцию в консоли браузера и изучите стек вызовов в различные моменты выполнения с помощью известных Вам инструментов.
В начало1 из 22ВыйтиЗавершить просмотр