Хранение переменных JS? Стек и куча? НЕТ НЕТ НЕТ!

внешний интерфейс
Хранение переменных JS? Стек и куча? НЕТ НЕТ НЕТ!

предисловие

этот разwhy what or howтема:JavaScriptпеременное хранилище.

Вне зависимости от языка переменные — основа всего, число — переменная, предмет — тоже переменная, вJavaScriptДаже функция in является переменной.

Тогда такая важная переменная, вJavaScriptКак именно хранится?

Стек и куча?

куча (Stack), также известный как стек, представляет собой линейную таблицу с ограниченным числом операций. Линейный список, который ограничивает вставки и удаления только концом списка.

Поиск на БайдуJavaScriptХранилище переменных, можно посмотреть много статей, не более чем вывод:

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

Однако я вдруг подумал о проблеме: если примитивный тип существует в стеке, тоJavaScriptКак замыкания реализованы в ?

Конечно, если вы хотите вникнуть в эту проблему, то надо сначала поставить стек (Stack) и куча (Heap), чтобы было понятно.

Ну, давайте сначала поговорим о стеке.

куча

Стек — это линейная структура в памяти, используемая для хранения локальных переменных и параметров функций в соответствии с принципом «первым пришел — последним вышел». Данные могут быть помещены в стек только последовательно и последовательно извлечены из стека. Конечно, стек — это просто формальное описание непрерывной области памяти, а операции добавления и извлечения данных в стек и из него просто перемещают указатель стека вверх и вниз по адресу памяти. Как показано на следующем рисунке (в качестве примера возьмем язык C):

变量在栈中存储

Как показано на рисунке, указатель стека только начинает указывать на память.0x001положение, тоsumНачинает вызываться функция, так как объявляются две переменные, в стеке сохраняются два значения, и указатель стека тоже начинает двигаться соответственно.sumВ конце вызова функции он просто перемещает указатель стека вниз, а не всплывающее окно с реальными данными, данные все еще там, но они будут перезаписаны при выполнении следующего присваивания.

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

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

куча

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

Чтобы в определенной степени решить проблему с кучей, некоторые языки высокого уровня (такие какJAVA) представляет концепцию:GC,Garbage Collection, сборка мусора, используемая для помощи программам в управлении памятью и активной очистке неиспользуемых данных в куче.

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

вопрос!

OKСтек и кучу содержимого как указано выше, теперь давайте посмотрим на вывод каждого:

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

Это кажется очень логичным.Согласно определению, базовый тип существует в стеке, а объект существует в куче.В этом нет ничего плохого!

Тем не менее, пожалуйста, подумайте над вопросом:

Так как данные в стеке будут уничтожены после завершения выполнения функции, тоJavaScriptКак реализовать замыкание функции посередине, сначала просто подойдите к замыканию:

function count () {
    let num = -1;
    return function () {
        num++;
        return num;
    }
}

let numCount = count();
numCount();
// 0
numCount();
// 1

Согласно заключению,numПеременная называетсяcountсоздается функция, вreturnвыскочил из стека.

Поскольку это логика, вызовитеnumCountкак выводится функция0Шерстяная ткань?numв функцииreturnвремя было уничтожено в памяти!

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

Значит, все в Интернете ошибаются? Не слишком! Далее поговорим о моемJavaScriptПонимание хранения переменных.

покинуть стек

так как вJavaScriptЕсть проблема с замыканиями, уходом из стека(Stack), можно ли реализовать хранилище переменных, используя только кучу? Рассмотрим частный пример:

function test () {
    let num = 1;
    let string = 'string';
    let bool = true;
    let obj = {
        attr1: 1,
        attr2: 'string',
        attr3: true,
        attr4: 'other'
    }
    return function log() {
        console.log(num, string, bool, obj);
    }
}

сопровождаемыйtestВызов, чтобы убедиться, что переменная не уничтожена, сначала генерирует объект в куче и вызывает егоScopeНу, поместите переменную какScopeсвойства сохраняются. Структура данных в куче выглядит примерно так:

使用 Scope 保存变量

Итак, это еще не решит проблему замыканий?

Конечно можно, потому чтоScopeОбъекты хранятся в куче, поэтому возвращаемыйlogфункция может иметьScopeдоступ к объектам. На картинке ниже код вChromeЭффект исполнения в:

Chrome 中 Scope 的表示

Часть красного прямоугольника, которая согласуется с вышеизложенным, также отражает проблемы, упомянутые ранее: в примереJavaScriptПеременная существует не в стеке, а в куче, используя специальный объект (Scope)спасти.

затем вJavaScriptКак именно переменные хранятся в процессе? Это напрямую связано с типом переменной, далее мы поговорим оJavaScriptТип переменной в .

три типа

существуетJavaScript, существует три типа переменных:

  1. Локальные переменные
  2. захваченная переменная
  3. глобальная переменная

локальная переменная

Локальные переменные хорошо понятны: объекты, объявленные в функции и не используемые другими областями видимости после возврата из функции. в коде нижеlocal*являются локальными переменными.

function test () {
    let local1 = 1;
    var local2 = 'str';
    const local3 = true;
    let local4 = {a: 1};
    return;
}

захваченная переменная

Захваченная переменная является противоположностью локальной переменной: если она объявлена ​​в функции, но все еще используется невыполненной областью (функция или класс) после возврата функции, то эта переменная является захваченной переменной. в коде нижеcatch*все захваченные переменные.

function test1 () {
    let catch1 = 1;
    var catch2 = 'str';
    const catch3 = true;
    let catch4 = {a: 1};
    return function () {
        console.log(catch1, catch2, catch3, catch4)
    }
}

function test2 () {
    let catch1 = 1;
    let catch2 = 'str';
    let catch3 = true;
    var catch4 = {a: 1};
    return class {
        constructor(){
            console.log(catch1, catch2, catch3, catch4)
        }
    }
}

console.dir(test1())
console.dir(test2())

скопировать код вChromeВы можете просмотреть объект вывода в разделе[[Scopes]]соответствующий нижеScope.

глобальная переменная

Глобальная переменнаяglobal, в браузерах какwindowсуществуетnodeЛивэйglobal. Глобальные переменные по умолчанию добавляются к нижнему концу цепочки областей действия функций, которой является функция выше.[[Scopes]]последний из .

Глобальные переменные требуют особого внимания:varа такжеlet/constразница.

var

Глобальныйvarпеременные простоglobalОбъект имеет добавленное свойство.

var testVar = 1;

// 与下述代码一致
windows.testVar = 1;

let / const

Глобальныйlet/constпеременные не изменяютсяwindowsобъект, объявление переменной помещается под специальный объект (сScopeаналогичный).

let testLet = 1;

console.dir(() => {})

скопировать вChromeсо следующими результатами:

let/const 全局变量

два пути

Затем определяется тип переменной, как ее сохранить? Есть два вида:

  1. куча (Stack)
  2. куча(Heap)

Я считаю, что когда вы видите это, всем должно быть ясно: кроме локальных переменных, все остальное хранится в куче! По типу данных переменной она делится на следующие два случая:

  1. Если это базовый тип, сами данные хранятся в стеке.
  2. Если это тип объекта, стек хранит ссылку на объект в куче.

Но это идеальная ситуация, поэтому позвольте задать вам еще один вопрос:JavaScriptКак синтаксический анализатор узнает, что переменная является локальной?

Определите, ссылается ли на него внутренняя функция!

что еслиJavaScriptПарсер не оценивает? Он может существовать только в куче!

Тогда вы должны спросить,ChromeизV8О том, можно ли судить, следует судить по результатам.

Chrome 下的局部变量

Только переменные в красном полеa, а переменнаяbисчез. из-заFireFoxне могу печатать[[Scopes]]свойства, следовательно, автор не может судить. Конечно, если найдутся большие ребята, которые смогут понять и добавить еще, буду очень благодарен.

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

присвоение переменной

На самом деле, независимо от того, находится ли переменная в стеке или в куче (во всяком случае, в памяти), ее структура аналогична способу хранения значения и имеет следующую структуру:

变量存储

Ну а теперь давайте посмотрим на присвоение, согласно=В правой части числа есть два типа переменных:

назначить как константу

Что такое константа? Константа — это значение, которое можно определить, как только оно будет объявлено, например1,"string",true,{a: 1}, являются константами. После объявления этих значений их нельзя изменить. Некоторые люди могут быть упрямыми. Тип объекта может быть константой, и его можно изменить. Я пока оставлю этот вопрос и объясню его позже.

Предположим, у вас сейчас есть следующий код:

let foo = 1;

JavaScriptобъявил переменнуюfoo, и пусть его значение будет1, в памяти произойдут следующие изменения

常量储存

Если теперь объявитьbarПеременная:

let bar = 2;

Тогда в памяти это будет выглядеть так:

foo & bar

Теперь вернемся к только что заданному вопросу: считаются ли типы объектов константами?

Например, следующий код:

let obj = {
    foo: 1,
    bar: 2
}

Модель памяти выглядит следующим образом:

JavaScript Object存储

Из этого рисунка мы можем узнать, что на самом делеobjУказанный адрес памяти также содержит значение адреса, и это хорошо, если мы позволимobj.foo = 'foo'На самом деле модифицированный0x1021в области памяти, ноobjАдрес памяти, на который указывает, не изменяется, следовательно, объект является константой!

Назначено переменным

Что такое переменная? в вышеописанном процессеfoo,bar,obj, являются переменными, переменные представляют собой ссылочное отношение, и их собственные значения не являются определенными.

Итак, что произойдет, если я присвою значение одной переменной другой переменной?

let x = foo;

x 赋值为 foo 变量

Как показано на рисунке выше, толькоxссылается сfooТолько одно и то же значение адреса, и новое пространство памяти использоваться не будет.

OKНа этом назначение заканчивается, и следующим шагом будет изменение.

переменная модификация

Как и присваивание переменных, модификация переменных также должна основываться на=В правой части числа есть два типа переменных:

изменить на постоянный

foo = 'foo';

foo 变量修改为另一常量

Как показано на рисунке выше, память сохраняется'foo'и воляfooСправочный адрес изменен на0x0204.

изменить на переменную

foo = bar;

foo 变量修改为另一变量

Как показано на рисунке выше, толькоfooУказанный адрес был изменен.

Как работает константа

constдляES6Способ для новых объявлений переменных бытьconstМодифицированные переменные не могут быть изменены.

на самом деле соответствуетJavaScriptВ карте хранения переменных адрес памяти, на который указывает переменная, не может быть изменен. То есть стрелку изменить нельзя.

Например следующий код:

const foo = 'foo';
foo = 'bar'; // Error

const 不允许重新赋值

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

Что ж, теперь вы можете решить свою путаницу со следующим кодом:

const obj = {
    foo: 1,
    bar: 2
};
obj.foo = 2;

ЭтоobjУказанный адрес не изменился, а измененная часть представляет собой другую область. Как показано ниже

const 对象类型修改

модификация объекта

OKОчень простые вопросы, которые можно задать при входе на собеседование:

let obj1 = {
    foo: 'foo',
    bar: 'bar'
}

let obj2 = obj1;
let obj3 = {
    foo: 'foo',
    bar: 'bar'
}

console.log(obj1 === obj2);
console.log(obj1 === obj3);

obj2.foo = 'foofoo';

console.log(obj1.foo === 'foofoo');

пожалуйста скажиconsoleрезультат.

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

js 中对象存储

Итак, теперь вы знаете ответ?

Суммировать

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

Как обычно, задайте несколько вопросов:

  1. JavaScriptКакие бывают типы переменных?
  2. JavaScriptКак хранятся примитивные типы и типы объектов?

Ссылаться на

последний из последних

Все выпуски этой серииminimoПредложи, я люблю тебя~~~