Иногда можно услышать «Все в Javascript объект». Не все является объектом в JavaScript. Но все, что не является примитивным значением, можно рассматривать как объект.

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

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

Память: стек и куча

В JavaScript жизненный цикл памяти состоит из 3 частей:

  1. Выделите необходимую память
  2. Использовать выделенную память (чтение, запись)
  3. Освобождать выделенную память, когда она больше не нужна

В JavaScript третья часть неявна, и эта роль предназначена для Сборщика мусора.

Нам нужно понять, как JavaScript Engine распределяет память, и именно здесь появляются стек (это не стек вызовов, стек — это просто структура данных) и куча.

Стек — это место, где хранятся статические данные движка. Статические данные — это данные, размер которых движку известен во время компиляции. В JavaScript это включает примитивные значения (строки, числа, логические значения, неопределенные и null) и ссылки, указывающие на объекты и функции.

У этих данных есть одна общая черта. Размер этих данных фиксирован, и JavaScript знает этот размер во время компиляции. Это также означает, что JavaScript знает, сколько памяти он должен выделить, и выделяет этот объем. Этот тип выделения памяти называется «статическим выделением памяти». И это происходит прямо перед выполнением кода.

Есть одна важная вещь о статических данных и памяти. Существует предел тому, насколько большими могут быть эти примитивные значения. Это справедливо и для самого стека. Это тоже имеет пределы. Насколько высоки эти ограничения, зависит от конкретного браузера и движка.

Куча — это пространство для хранения динамических данных, где JavaScript хранит объекты (ссылочные значения), такие как массивы, объекты, функции и т. д. Этот тип распределения памяти называется «динамическим выделением памяти».

Давайте посмотрим на пример кода ниже:

А вот как это выглядит в памяти:

Рекомендации

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

Все переменные сначала указывают на стек памяти. Когда вы создаете примитивное значение, JavaScript Engine сохраняет его в стеке памяти, но когда вы создаете объект (не примитивное значение), стек памяти содержит ссылку на объект в куче памяти.

Мутация

Мутация означает изменение формы или характера исходных данных.

Ссылочные типы (объекты, массивы и т. д.) в JavaScript изменяемы, это означает, что они затрагивают существующие экземпляры, а не создают их копии.

Как мы уже говорили ранее, стек памяти просто удерживает [Pointer] в куче памяти, и несколько [Pointers] могут быть связаны с одним объектом в куче памяти.

В приведенном выше примере наши someFlat, joe.flat и nate.flat — это 3 разных [Pointer]'s, но связанных с одним и тем же объектом в куче памяти.

Причина, по которой наши изменения также применяются ко всем someFlat, joe.flat и nate.flat, заключается в том, что они представляют собой один и тот же объект, только с разными ссылками/указателями.

Мутация — это плохая практика программирования. Если ваш код изменчив, вы можете изменить или сломать что-то, не зная об этом. Но иногда они нам нужны.

Подробнее:

Копирование объектов

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

У нас есть несколько способов копирования объектов в JavaScript, и все они имеют ограничения, о которых вам нужно знать:

  1. Используйте синтаксис спреда (...) (docs)
  2. Используйте метод Object.assign() (docs)
  3. Используйте методы JSON.stringify() и JSON.parse() (docs)
  4. Используйте метод structuredClone() (docs)

Проблема сJSON.stringify

Нам нужно знать, что некоторые значения не безопасны для JSON, например: undefined, symbols и function.

Утилита JSON.stringify(..) автоматически пропустит неопределенные значения, значения функций и символов, когда встретится с ними.

Если такое значение найдено в массиве, это значение заменяется нулем (так что информация о позиции массива не изменяется). Если оно найдено как свойство объекта, это свойство будет просто исключено.