JavaScript Overview — Objects and Memory
Sometimes you can hear “Everything in Javascript an object”. Not everything is an object in JavaScript. But everything that is not a primitive value can be considered as an object.
As I mentioned in the previous part objects are non-primitive/reference data types. And they behave differently then primitives.
And before we can continue we need to understand how JavaScript stores values in memory.
Memory: Stack and Heap
In JavaScript memory life cycle consist of 3 parts:
- Allocate the memory you need
- Use the allocated memory (read, write)
- Release the allocated memory when it is not needed anymore
In JavaScript 3rd part is implicit and this role is for the Garbage collector.
We need to understand how JavaScript Engine allocate the memory and and that’s where Stack (it’s not a Call Stack, Stack is just a data strucrure) and Heap show up.
Stack is where engine static data. Static data is data where the engine knows the size at compile time. In JavaScript, this includes primitive values (strings, numbers, booleans, undefined, and null) and references, which point to objects and functions.
These data have one thing in common. The size of these data is fixed and JavaScript knows this size at compile time. This also means that JavaScript knows how much memory it should allocate, and allocates that amount. This type of memory allocation is called “static memory allocation”. And it happens right before the code is executed.
There is one important thing about static data and memory. There is a limit to how large these primitive values can be. This is also true for the stack itself. That too has limits. How high these limits are depends on specific browser and engine.
Heap is a space for storing dynamic data where JavaScript stores objects (references values) like arrays, objects, functions and etc. This type of memory allocation is called “dynamic memory allocation”.
Let’s look at code example below:
And here how it looks in memory:
References
Object values or non-primitive values (arrays, objects, functions, etc.) are treated as references. References are the idea that two or more variables are pointing at the same value.
All variables first point to the Memory Stack. When you create a primitive value JavaScript Engine store it in Memory Stack, but when you create an object (non-primitive value) the Memory Stack contains a reference to the object in the Memory Heap.
Mutation
Mutation means a change in the form or nature of the original data.
Reference types (Objects, Arrays, etc.) in JavaScript are mutable, it means affecting existing instance and not creating a copy of them.
As we sow before, Memory Stack just hold the [Pointer]
to the Memory Heap and few [Pointers]
can be linked to one object in Memory Heap.
In example above our someFlat
, joe.flat
and nate.flat
are 3 different [Pointer]'s
but the linked to the same object in Memory Heap.
The reason why our changes also applied to all someFlat
, joe.flat
and nate.flat
too and it’s because they are just the same object just with different references/pointers.
The mutation is a bad programming practice. If your code is mutable, you might change or break something without knowing. But sometimes we need them.
More:
- Primitive vs Reference Data Types in JavaScript
- Arrays, Objects and Mutations
- The Secret Life of Objects
- JavaScript’s Memory Management Explained
Copying objects
In JavaScript no now easy way to create a copy of an object. Trying to create a copy of an object stored in a variable by referencing it will not create real copy. It will not copy the object itself. It will copy only reference to that object. We sow it before.
We have few ways how we can copy objects in JavaScript and all of them have limitations and you need to know about them:
- Use the spread (
...
) syntax (docs) - Use the
Object.assign()
method (docs) - Use the
JSON.stringify()
andJSON.parse()
methods (docs) - Use the
structuredClone()
method (docs)
The problem with JSON.stringify
We need to know that some values are not JSON-safe, like: undefined
, symbols
, and function
.
The JSON.stringify(..)
utility will automatically omit undefined, function, and symbol values when it comes across them.
If such a value is found in an array, that value is replaced by null (so that the array position information isn’t altered). If found as a property of an object, that property will simply be excluded.