Чем больше вы знаете, тем больше вы не знаете
点赞
Посмотри еще раз, аромат остался в руке, и слава
введение
Пространство памяти JS разделено на стек (стек), куча (куча), пул (обычно также классифицируется как стек).
В стеке хранятся переменные, в куче — сложные объекты, а в пуле — константы, поэтому его еще называют константным пулом.
структура данных стека
Стек — это особый вид списка, и доступ к элементам стека возможен только через один конец списка, который называется вершиной стека. Стек известен как структура данных «последний пришел – первый вышел» (LIFO, «последний пришел – первый ушел»). Из-за того, что стек работает по принципу «последним пришел — первым вышел», доступ к любому элементу, который не находится на вершине стека, невозможен. Чтобы получить элемент в нижней части стека, необходимо сначала удалить элемент выше.
Здесь для удобства понимания метод доступа к стеку анализируется по аналогии с коробкой для пинг-понга.
Этот шарик для пинг-понга хранится точно так же, как данные хранятся в стеке. Мяч для пинг-понга 5, который является самой верхней коробкой в коробке, должен быть помещен в последнюю очередь, но может быть использован первым. И если мы хотим использовать нижний шарик для пинг-понга 1, мы должны вынуть 4 верхних шарика для пинг-понга, чтобы шарик для пинг-понга 1 оказался наверху коробки. Это характеристика пространства стека FIFO и LIFO.
структура данных кучи
Куча — это отсортированная древовидная структура данных, каждый узел которой имеет значение. Обычно то, что мы называем структурой данных кучи, относится к двоичной куче. Характеристика кучи состоит в том, что значение корневого узла является наименьшим (или наибольшим), а два поддерева корневого узла также являются кучей. Из-за этой особенности кучи ее часто используют для реализации приоритетных очередей, а доступ к куче произвольный, как когда мы берем книги с книжных полок в библиотеке, Хотя книги расположены по порядку, когда мы хотим взять любую книгу, нам не нужно сначала вынимать все предыдущие книги, как стопку. Нам нужно только позаботиться о названии книги.
Связь между типами переменных и памятью
базовый тип данных
Существует 6 основных типов данных:
- Sting
- Number
- Boolean
- null
- undefined
- Symbol
Базовые типы данных хранятся в памяти стека.Поскольку базовые типы данных занимают мало места и имеют фиксированный размер, доступ к ним осуществляется по значению и они являются часто используемыми данными.
Чтобы лучше понять основные переменные типов данных и стековую память, мы объединим следующие примеры и диаграммы, чтобы понять:
let num1 = 1;
let num2 = 1;
PS: Следует отметить, что базовые переменные типа данных в замыкании хранятся не в памяти стека, а в памяти кучи. Мы обсудим этот вопрос позже.
ссылочный тип данных
Массив, функция, объект... Можно считать, что все типы, кроме упомянутых выше основных типов данных, являются ссылочными типами данных.
Справочные типы данных хранятся в куче памяти, поскольку ссылочные типы данных занимают много места и не имеют фиксированного размера. Если он хранится в стеке, это повлияет на производительность программы; Ссылочный тип данных хранит в стеке указатель, указывающий на начальный адрес объекта в куче. Когда интерпретатор ищет ссылочное значение, он сначала извлекает его адрес в стеке, а затем получает сущность из кучи после получения адреса.
Чтобы лучше понять переменные объекты и память кучи, мы объединим следующие примеры и диаграммы для понимания.
// 基本数据类型-栈内存
let a1 = 0;
// 基本数据类型-栈内存
let a2 = 'this is string';
// 基本数据类型-栈内存
let a3 = null;
// 对象的指针存放在栈内存中,指针指向的对象存放在堆内存中
let b = { m: 20 };
// 数组的指针存放在栈内存中,指针指向的数组存放在堆内存中
let c = [1, 2, 3];
Поэтому, когда мы хотим получить доступ к ссылочному типу данных в куче памяти, мы фактически сначала получаем адресный указатель объекта из переменной, Затем получаем нужные нам данные из кучи памяти.
Копирование переменных с точки зрения памяти
Репликация основных типов данных
let a = 20;
let b = a;
b = 30;
console.log(a); // 此时a的值是多少,是30?还是20?
Ответ: 20
В этом примере a и b оба являются базовыми типами, их значения хранятся в памяти стека, a и b имеют свое независимое стековое пространство, Таким образом, после изменения значения b значение a не изменится.
На рисунке ниже хорошо видно, как копируются и изменяются переменные.
Копия ссылочного типа данных
let m = { a: 10, b: 20 };
let n = m;
n.a = 15;
console.log(m.a) //此时m.a的值是多少,是10?还是15?
Ответ: 15
В этом примере m и n являются ссылочными типами, а адрес в памяти стека указывает на объект в памяти кучи. Копирование ссылочного типа автоматически присвоит новое значение новой переменной и сохранит его в переменной, Но это всего лишь адресный указатель ссылочного типа, фактически он указывает на один и тот же объект, Таким образом, после изменения значения n.a соответствующее m.a также изменяется.
На рисунке ниже хорошо видно, как копируются и изменяются переменные.
Преимущества и недостатки памяти стека и памяти кучи
В JS основные переменные типа данных имеют фиксированный размер и просты в использовании, поэтому они хранятся в стеке. Размер переменных ссылочного типа не является фиксированным, поэтому они выделяются в кучу и позволяют им определять размер при обращении за пространством, чтобы их можно было хранить отдельно, чтобы программа работала с наименьшим объемом памяти.
Благодаря своим характеристикам стековая память имеет более высокую системную эффективность. Память кучи должна выделять пространство и адрес, а также хранить адрес в стеке, поэтому эффективность ниже, чем у стека.
Сборка мусора из стека и кучи памяти
Переменные в памяти стека обычно уничтожаются и перерабатываются сборщиком мусора в конце их текущей среды выполнения. Переменные в памяти кучи - нет, потому что неизвестно, есть ли ссылки на них где-либо еще. Переменная в куче памяти восстанавливается только тогда, когда все ссылки на нее закончились.
Подробнее о сборке мусора читайте в другой статье (управление памятью в JS)
Замыкания и куча памяти
Переменные в замыкании хранятся не в памяти стека, а в памяти кучи. Это также объясняет, почему замыкание может ссылаться на переменные в функции после вызова функции.
Давайте сначала посмотрим, что такое замыкание:
function A() {
let a = 1;
function B() {
console.log(a);
}
return B;
}
let res = A();
Функция A возвращает функцию B, а функция B использует переменные функции A, функция B называется замыканием.
После того, как функция A извлекает стек вызовов, переменные в функции A в это время сохраняются в куче, поэтому функция B все еще может ссылаться на переменные в функции A. Современные JS-движки могут определить, какие переменные нужно хранить в куче, а какие — в стеке, с помощью escape-анализа.
Рекомендуемая серия статей
- Анализ и реализация одностраничной маршрутизации «переднего плана»
- «Front-end Advanced» полностью понимает каррирование функций.
- «Расширенное переднее» управление памятью в JS
- Массив "Front-end advanced" вышел из строя
Ссылаться на
напиши в конце
- Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, добро пожаловать
点赞
а также关注
- Эта статья была впервые опубликована одновременно сgithub, доступны наgithubНайдите больше отличных статей в
Watch
&Star ★
- Для последующих статей см.:строить планы
Добро пожаловать в публичный аккаунт WeChat
【前端小黑屋】
, 1–3 высококачественные высококачественные статьи публикуются каждую неделю, чтобы помочь вам в продвижении вперед.