Иногда можно услышать «Все в Javascript объект». Не все является объектом в JavaScript. Но все, что не является примитивным значением, можно рассматривать как объект.
Как я упоминал в предыдущей части, объекты являются непримитивными/ссылочными типами данных. И они ведут себя иначе, чем примитивы.
И прежде чем мы сможем продолжить, нам нужно понять, как JavaScript хранит значения в памяти.
Память: стек и куча
В JavaScript жизненный цикл памяти состоит из 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
Копирование объектов
В JavaScript сейчас нет простого способа создать копию объекта. Попытка создать копию объекта, хранящегося в переменной, путем ссылки на него не создаст реальную копию. Он не будет копировать сам объект. Он будет копировать только ссылку на этот объект. Мы сеем его раньше.
У нас есть несколько способов копирования объектов в JavaScript, и все они имеют ограничения, о которых вам нужно знать:
- Используйте синтаксис спреда (
...
) (docs) - Используйте метод
Object.assign()
(docs) - Используйте методы
JSON.stringify()
иJSON.parse()
(docs) - Используйте метод
structuredClone()
(docs)
Проблема сJSON.stringify
Нам нужно знать, что некоторые значения не безопасны для JSON, например: undefined
, symbols
и function
.
Утилита JSON.stringify(..)
автоматически пропустит неопределенные значения, значения функций и символов, когда встретится с ними.
Если такое значение найдено в массиве, это значение заменяется нулем (так что информация о позиции массива не изменяется). Если оно найдено как свойство объекта, это свойство будет просто исключено.