Английское название стека вызовов называется Call Stack, и все более или менее слышали его, но большинство людей, возможно, не проводили более глубокого исследования того, как работает стек вызовов js и как использовать эту функцию в работе. Часть контента можно назвать так называемым базовым знанием для нашего интерфейса, На первый взгляд, оно кажется не очень полезным, но освоение этого пункта знаний позволит нам идти дальше и двигаться быстрее в будущем!
содержание
- Структура данных: стек
- 调用栈是什么? для чего это используется?
- Как работает стек вызовов
- память, оптимизированная для стека вызовов
- Метод отладки стека вызовов
Структура данных: стек
стек является соответствиемпоследний вошел первый вышел (LIFO
) упорядоченный набор принципов, новые элементы находятся ближе к вершине стека, а старые элементы ближе к концу стека.
Каштаны в жизни помогают понять:
Тарелки (стопки), сложенные в ресторане, кладутся внизу в начале (первыми вносятся), а те, что сзади, помещаются вверху (последними вставляются).
Что такое стек вызовов? для чего это используется?
- Стек вызовов представляет собой данные со структурой стека, которые состоят из вызовов.
- В стек вызовов записывается такая информация, как порядок выполнения функции и внутренние переменные функции..
Как работает стек вызовов
механизм:
Когда программа обращается к функции, она добавляет ее в стек вызовов, а когда она возвращается из функции, она удаляет функцию из стека вызовов.
Взгляните на пример, чтобы помочь понять:
// 调用栈中的执行步骤用数字表示
printSquare(5); // 1 添加
function printSquare(x) {
var s = multiply(x, x); // 2 添加 => 3 运行完成,内部没有再调用其他函数,删掉
console.log(s); // 4 添加 => 5 删掉
// 运行完成 删掉printSquare
}
function multiply(x, y) {
return x * y;
}
Выполнение следующих шагов в стеке вызовов (многократно удаляемые шаги опущены):
обнаружение вызова:
Каждой функции, которая входит в стек вызовов, будет выделено отдельное пространство стека, называемое «обнаружение вызова».
В стеке вызовов каждый «вызывающий» соответствует функции, верхний кадр вызова называется «текущий кадр», а стек вызовов формируется всеми вызывающими объектами.
Найти изображение и вызвать обнаружение:
память, оптимизированная для стека вызовов
Потребление памяти стеком вызовов:
Как показано на рисунке выше, переменные и другая информация функции будут сохранены вызывающей стороной, поэтомуПеременные в вызывающем объекте не будут собираться сборщиком мусора..
Когда уровень вложенности функций относительно глубок и в стеке вызовов много обнаружений вызовов, эта информация потребляет много памяти.
В ответ на эту ситуацию, в дополнение к попытке избежать глубоко вложенных уровней функций, ES6 обеспечивает «оптимизацию хвостовых вызовов», чтобы решить проблему чрезмерного потребления памяти, вызванную чрезмерным обнаружением вызовов.
Что такое хвостовой вызов:
Хвостовой вызов относится к:Последним шагом функции является вызов другой функции..
function f(x){
return g(x); // 最后一步调用另一个函数并且使用return
}
function f(x){
g(x); // 没有return 不算尾调用 因为不知道后面还有没有操作
// return undefined; // 隐式的return
}
Что оптимизирует оптимизация хвостовых вызовов?
хвостовой вызовУдалить внешнее бесполезное обнаружение вызова, только обнаружение вызова внутренней функции зарезервировано для сохранения памяти браузера.
В следующем примере в стеке вызовов всегда есть только один детектор вызовов.Если хвостовые вызовы не используются, детекторов вызовов будет три:
a() // 1 添加a到调用栈
function a(){
return b(); // 在调用栈中删除a 添加b
}
function b(){
return c() // 删除b 添加c
}
Предотвратить взрыв стека:
пара браузераСтек вызовов имеет ограничение по размеру, если рекурсия была глубокой до ES6, легко возникла проблема «переполнения стека».
Теперь можно использовать «оптимизацию хвостового вызова» для написания «хвостовой рекурсии», которая сохраняет только одно обнаружение вызова, чтобы предотвратить проблемы с взрывом стека.
Уведомление:
- Только когда внутренние переменные внешней функции больше не используются, кадр вызова внутренней функции заменит кадр вызова внешней функции.
Если вы хотите использовать переменные внешней функции, вы можете передать их внутренней функции в виде параметра
function a(){
var aa = 1;
let b = val => aa + val // 使用了外层函数的参数aa
return b(2) // 无法进行尾调用优化
}
- Оптимизация хвостового вызова включена только в строгом режиме, нестрогий режим недействителен.
- Если среда не поддерживает «оптимизацию хвостового вызова», код все равно будет работать нормально, ничего страшного!
Более:
О хвостовой рекурсии и дополнительной оптимизации хвостовых вызовов рекомендуется прочитатьНачало работы с ES6 — Жуан Ифэн
Метод отладки стека вызовов
Какая польза от просмотра стека вызовов
-
Проверьте, согласуется ли последовательность вызова функций с ожиданиями, например вызов разных функций в соответствии с разными суждениями.
-
Быстро найти проблему/изменить код сторонней библиотеки.
Когда вы берете на себя исторический проект или у вас возникают проблемы со сторонней библиотекой, вы можете сначала проверить стек вызовов соответствующего API, найти задействованные ключевые функции и исправить их.
Просмотр формы стека вызовов помогает мне быстро найти проблему и изменить исходный код сторонней библиотеки.
Как просмотреть стек вызовов
- Просто посмотрите на стек вызовов:
console.trace
a()
function a() {
b();
}
function b() {
c()
}
function c() {
let aa = 1;
console.trace()
}
Как показано на рисунке, щелкните справа, чтобы просмотреть расположение кода:
-
bugger
Форма точки останова, которая также является моим любимым способом отладки:
Знакомство с тысячей миль
Обычно нужно сознательно делать такого рода небольшую оптимизацию (я сейчас), пытаться писать код наилучшей практики.
Когда проект небольшой, это может не иметь значения.Когда проект большой, особенно когда какие-то маленькие методы склеены и вложены в большой вывод API, потребление памяти в стеке вызовов будет огромным! Такую оптимизацию тоже нельзя недооценивать, она занимает несколько шагов и даже тысячи километров, давайте подбадривать друг друга!
Эпилог
В этой статье в основном говорится об этих аспектах:
- Понимая механизм работы стека вызовов, мы также можем лучше понять некоторые механизмы выполнения кода, что поможет нам двигаться дальше.
- Мы должны сознательно использовать «оптимизацию хвостовых вызовов» ES6 в нашем ежедневном коде, чтобы уменьшить длину стека вызовов и сэкономить память клиента.
- Используя стек вызовов для сторонних библиотек или незнакомых проектов, мы можем быстрее обнаруживать проблемы и повышать скорость отладки.
Наконец: я уже писал статью оМеханизм сборки мусора и утечка памятиСтатья, заинтересованные студенты могут расширить.
Если эта статья помогла вам, пожалуйста, поставьте лайк и подпишитесь, ваша поддержка - самая большая поддержка для меня!
Внешние документы расширенного накопления,Нет публики,GitHub
Выше 2019/5/19
Использованная литература:
Механизм сборки мусора JS и решения распространенных утечек памяти
Начало работы с ES6 — Жуан Ифэн
Как работает JavaScript: обзор движка, среды выполнения, стека вызовов