
Data Structures Deep Dive: Objects, Arrays & Immutability
Abhay Vachhani
Developer
If you've ever spent hours debugging why changing one variable accidentally updated another, you've been bitten by JavaScript's reference system. Understanding how JavaScript handles data in memory is the single most important step in moving from a junior to a senior backend developer. It's not just about syntax; it's about predictability and performance.
1. Reference vs Value Types (The Root of All Bugs)
In JavaScript, data types describe how they are stored in memory. Primitives (String, Number, Boolean, null, undefined) are stored by value. Objects (including Arrays and Functions) are stored by reference.
// Value Type (Safe)
let a = 10;
let b = a;
b = 20;
console.log(a); // 10 - Unchanged
// Reference Type (Dangerous?)
let user1 = { name: "Abhay" };
let user2 = user1; // Copies the REFERENCE, not the object
user2.name = "Updated";
console.log(user1.name); // "Updated" - Surprise!
When you assign an object to a new variable, you aren't creating a copy. You are creating a second remote control pointing to the same TV. Modifying one modifies the "truth" in memory.
2. The Mutability Trap of Built-in Methods
Many built-in Array methods mutate the array in place. This is a common source of bugs in React and state-heavy Node.js apps.
- Mutating:
push,pop,shift,unshift,splice,sort,reverse. - Safe (Returns new array):
map,filter,slice,concat,reduce.
The "New" Way (2023+)
Modern JavaScript (ES2023) introduced non-mutating versions of these classics. Use these to keep your data pure:
const nums = [3, 1, 2]; // Old Way (Mutates nums!) // nums.sort(); // New Way (Returns new array, keeps nums intact) const sorted = nums.toSorted(); const reversed = nums.toReversed(); const spliced = nums.toSpliced(0, 1);
3. Shallow vs Deep Copies & JSON Pitfalls
To safely duplicate data, you need to understand the difference between shallow and deep copies.
Shallow Copy
Methods like Object.assign() or the spread operator ... create shallow copies. They copy the top-level properties but keep references for nested objects.
Deep Copy & JSON Pitfalls
A deep copy recursively clones every level. Historically, developers used JSON.parse(JSON.stringify(obj)). While fast, it is lossy:
Dateobjects become keyless Strings.undefinedandFunctionkeys are completely removed.NaNandInfinitybecomenull.
The Fix: Use structuredClone(obj). It handles Dates, Sets, Maps, and circular references correctly.
4. Immutability: Freeze vs Seal
const only prevents variable reassignment, not object mutation. To truly protect an object, use:
- Object.preventExtensions(obj): No new properties can be added. Existing ones can be deleted or modified.
- Object.seal(obj): Like above, but existing properties cannot be deleted. Values can still be changed.
- Object.freeze(obj): The strictest level. No additions, deletions, or value changes. Note: It is shallow! Nested objects can still be mutated unless you deep freeze recursively.
5. Performance Check: The `delete` Operator
Using the delete keyword (e.g., delete user.name) is syntactically correct but practically disastrous for high-performance code.
V8 (Node's engine) uses "Hidden Classes" to optimize object access. When you delete a property, you change the shape of the object, forcing V8 to de-optimize it and treat it as a slower "dictionary" mode. This can be 15x-100x slower in tight loops.
Better Practice: Set the value to null or undefined instead, or use a Map if you need frequent additions/removals.
6. Maps and Sets vs Objects and Arrays
For a long time, we abused plain Objects as hashmaps. Modern JavaScript gives us better tools.
Map vs Object
- Keys: Objects only allow Strings/Symbols. Maps allow any type (objects, functions) as keys.
- Order: Maps preserve insertion order. Objects make no guarantees (though typically insertion order for strings).
- Performance: Maps are optimized for frequent additions and removals (see the `delete` point above).
Set vs Array
A Set is a collection of unique values. It's the fastest way to deduplicate an array.
const unique = [...new Set([1, 2, 2, 3])]; // [1, 2, 3]
Conclusion
Your data is the foundation of your application. By understanding how references work, avoiding mutations with modern methods like toSorted, using structuredClone, and respecting the hidden costs of operations like delete, you write code that is safer and significantly more performant.
FAQs
Why should I avoid JSON.stringify for deep copies?
It loses data types. Dates become strings, and undefined/functions are stripped entirely. Use `structuredClone` instead.
Is delete really that slow?
Yes, because it breaks V8 optimization (hidden classes). For simple apps, it doesn't matter, but for high-performance backend processing, it adds up.
What is the difference between freeze and seal?
`freeze` makes an object completely read-only. `seal` prevents adding/removing keys but allows changing the values of existing keys.