Анализ кучи JS, стека, стека выполнения и EventLoop

JavaScript
Анализ кучи JS, стека, стека выполнения и EventLoop

В настоящее время большинство фронтенд-интервью будут задавать вопросы о цикле событий, стеке выполнения и т. д. Эта статья объяснит вам эти концепции в виде кейсов, рисунков и т. д. Если вы внимательно прочитаете ее, я полагаю, что 90% студентов могут полностью понять это.

Механизм памяти JS

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

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

существуетJSДля каждых данных требуется место в памяти, а пространство памяти делится на стековую память (stack) и динамическую память (heap).

Память стека обычно хранит основные типы данных

Number String Null Undefined Boolean Symbol

См. пример:

var num = 1 

мы определяем переменнуюnum, система автоматически выделяет место для хранения. Мы можем напрямую манипулировать значениями, хранящимися в пространстве памяти стека, поэтому доступ к базовым типам данных осуществляется по значению.

Хранение и использование данных в памяти стека аналогично структуре данных стека в структуре данных.последний пришел первый вышелправила.

Куча памяти обычно хранит ссылочные типы данных

var user = { name:'jack' }
var arr = [1,3,5]

JSссылочные типы данных, такие как массивыArray, размер их значений не фиксирован. Значения ссылочных типов данных — это объекты, которые хранятся в куче памяти.JavaScriptПрямой доступ к местам в куче памяти не разрешен, поэтому мы не можем напрямую манипулировать пространством кучи памяти объекта.

Благодаря следующей картинке мы можем интуитивно понять.

var num = 1;   // 栈 
var name = '前端未来'; // 栈

// 变量user存在于栈中,{name:'河畔'}存在于堆内存中
var user = { name: '河畔' }; 
// 变量arr存在于栈中,[1, 2, 3] 作为对象存在于堆内存中
var arr = [1, 3, 5]; 

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

Итак, мы часто говорим: основные типы присвоения не влияют друг на друга, присвоение ссылочного типа будет влиять на исходный объект.

Пример прояснит:

var a = 20;
var b = a;
b = 30;
// a为20,b为30,值类型不影响
console.log(a) 

var user = { name: '河畔' }; 
var info = user;
info.name = 'Jack'
// 打印为jack,指向同一个内存地址
console.log(user.name)

Суммировать:

  • JavaScript имеет автоматический механизм сборки мусора
  • Память JS делится на память кучи и память стека.
  • Ссылочные типы содержат указатели в стеке и значения объектов в куче
  • Данные памяти стека следуютпервый вышел последним

EventLoop

Сейчас на фронтенд-интервью все любят спрашивать EventLoop, но, честно говоря, многие прочитали бесчисленное количество статей и до сих пор в замешательстве, сегодня они до сих пор демонстрируют эффект через код + картинки.

Чтобы лучше понять механизм событий, нам нужно сначала представить стек выполнения. Все прогоны JS-кода запускаются и выполняются после push и pop до тех пор, пока стек не опустеет.

стек выполнения

Код JS создаст контекст выполнения перед запуском, который также можно понимать как среду выполнения.В JS есть три контекста выполнения:

  • Глобальным контекстом выполнения по умолчанию является объект окна в браузерах.
  • Контекст выполнения функции, JS-функции создают контекст при каждом вызове.
  • Eval выполняет контекст, а функция eval генерирует свой собственный контекст.

Обычно в нашем коде имеется более одного контекста, так каким же должен быть порядок выполнения этих контекстов? Выполнять сверху вниз?

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

Далее рассмотрим пример:

function foo() {
  console.log('1');
  bar();
  console.log('3');
}
function bar() {
  console.log('2');
}
foo();

В этом нет никаких сомнений, ответ знают все, как называется стек выполнения?

Сначала выполните этот файл JS, создайте глобальный контекст и поместите его в стек выполнения.Когда вызывается функция foo(), поместите контекст выполнения функции foo в стек выполнения, а затем выполните вывод «1»; когда функция bar() вызывается Called, поместите контекст выполнения функции bar в стек выполнения, а затем выполните вывод '2'; после выполнения bar() он извлекается из стека выполнения, а foo () продолжает выполняться, выводя '3'; выполнение функции foo() завершено, она выталкивается из стека выполнения, и, наконец, весь стек выполнения очищается.Это первый вход последним, Foo сначала помещается в стек выполнения и, наконец, выталкивается из стека выполнения

EC — это контекст выполнения Execute Context.

Суммировать:

  • Весь код JS должен быть помещен в стек выполнения для запуска.
  • Существует три контекста выполнения (глобальный, функциональный, eval)
  • Стек — это структура данных, которая следуетпервый вышел последним

Затем посмотрите на классический вопрос интервью

console.log(1)
new Promise(function(resolve){
    console.log(3)
    resolve(100)
}).then(function(data){
    console.log(data)
})
setTimeout(function(){
    console.log(4);
})
console.log(2)

Приведенный выше результат печати вопроса интервью: 1 3 2 100 4

Можете ли вы назвать конкретные шаги его реализации?

Мы все знаем, что JS сам по себе является однопоточным и может делать только одну вещь за раз, так как же он справляется с таймерами и промисами? На самом деле, мы представимqueneОчередь.

Основной поток выполняет синхронные блоки кода, и когда он сталкивается с асинхронными задачами, такими как таймеры и промисы, он создает очередь событий и бросает их в очередь, а после завершения основного потока возвращается в очередь выполнения.task.

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

В JS браузера асинхронные задачи делятся на макрозадачи и микрозадачи, причем как макрозадачи, так и микрозадачи принадлежат очереди, а не помещаются в стек. Микрозадача создаст очередь, макрозадача создаст очередь, и после выполнения основного потока она сначала выполнит микрозадачу, поместит все микрозадачи в стек выполнения для выполнения и, наконец, возьмет одну из них. задачу макроса и положить ее в стек выполнения Выполнить, после выполнения взять еще одну, пока не будут выполнены все задачи макроса.

Далее посмотрите на картинку:

Диаграмма JS слева содержит кучу и стек, и весь код будет помещен в стек для выполнения, мы называем этостек выполнения, Стек выполнения является основным потоком. Сначала он выполняет синхронные задачи. Когда асинхронные задачи, такие как ajax и setTimeout, встречаются в середине, он будет помещен в очередь. Наконец, события в очереди будут извлечены и помещены в исполнение для выполнения, и процесс будет циклически повторяться.

Тогда давайте снова посмотрим на приведенный выше пример:

console.log(1)
new Promise(function(resolve){
    console.log(3)
    resolve(100)
}).then(function(data){
    console.log(data)
})
setTimeout(function(){
    console.log(4);
})
console.log(2)
  1. Создайте глобальный контекст и поместите его в стек выполнения.
  2. Нажмите синхронный код Console.log в стек выполнения для выполнения, печать1, и извлеките стек
  3. Поместите синхронный код new Promise в стек выполнения для выполнения, распечатайте3, и извлеките стек

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

  1. затем — асинхронная задача, поместите ее в очередь микрозадач и создайте событие
  2. setTimeout — это асинхронная задача, поместите ее в очередь задач макроса и создайте событие.

Примечание: задача очереди макро- и микрозадач состоит из двух

  1. Поместите синхронный код console.log в стек выполнения для выполнения, распечатайте2, и извлеките стек

На данный момент во всем стеке выполнения остается только глобальный контекст, а исполняемый код отсутствует.

  1. Микрозадачи выполняются первыми, поэтому все события в очереди микрозадач извлекаются и помещаются в стек выполнения для выполнения. Распечатать100, и извлеките стек
  2. Из очереди задач макроса взять только одно событие и поместить его в стек выполнения для выполнения, вывести4

Итак, давайте изменим приведенный выше пример:

console.log(1)
new Promise(function(resolve){
    console.log(3)
    resolve(100)
}).then(function(data){
    console.log(data)
}).then(function(){
    console.log(200)
})
setTimeout(function(){
    console.log(4);
})
setTimeout(function(){
    console.log(5);
})
console.log(2)

Есть 2 then и 2 setTimeouts.После обучения, как вы думаете, сколько должно быть напечатано? Ответ: 1 3 2 100 200 4 5

Это конец всей статьи, надеюсь, вы ее поняли!

Суммировать:

  • JavaScript имеет автоматический механизм сборки мусора
  • Память JS делится на память кучи и память стека.
  • Ссылочные типы содержат указатели в стеке и значения объектов в куче
  • Весь код JS должен быть помещен в стек выполнения для запуска.
  • Перед выполнением кода создается контекст выполнения
  • Существует три контекста выполнения (глобальный, функциональный, eval)
  • Синхронные задачи выполняются первыми, а асинхронные ставятся в очередь.
  • Сначала выполняются микрозадачи, а затем макрозадачи.
  • Все микрозадачи загружаются в стек выполнения, а макрозадачи загружаются по одной.
  • Стек приходит первым, уходит последним, а очередь поступает первым

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

Вышеупомянутые концепции кучи, стека, механизма событий и т. Д. Организованы для всех. Я надеюсь, что каждый может сказать «Чжан Сан» и «Ли Силай» во время интервью и не будет отвергнут другой стороной.

Официальный аккаунт WeChat: Будущее интерфейсов

Личный WeChat:

В этой статье используетсяmdniceнабор текста