Object Reference Mechanics

The Assignment Rule

When we assign a variable to another variable (let a = b), the engine does not look inside the data. It blindly copies the Stack Slot.

A. Primitives (Copy by Value)

When we assign a Primitive, the engine copies the actual value.

  • Result: The two variables become completely independent. Changing one does not affect the other.
let a = 10;
let b = a; // Copy the value 10
b = 20; // a is still 10

B. Objects (Copy by Reference)

When we assign an Object, the engine copies the Memory Address stored in stack slot, not the Heap data.

  • Result: Both variables point to the same object in the Heap. This is called Aliasing.
let user1 = { name: "Singh" }; // Address: 0x001
let user2 = user1; // Copy the Address 0x001

Object Comparison Mechanics

Because objects live in the Heap, comparison operators do not look at the properties inside the object. They look at the addresses stored in the Stack.

1. The Reference Check

Two objects are only equal if they point to the same physical memory address.

  • {} === {} is false because each literal creates a new unique address in the Heap.
  • objA === objB is true only if they share the same reference.

2. Double (==) vs. Triple (===) Equals

The comparison behavior changes fundamentally depending on whether the engine stays in the Stack or triggers a Coercion routine into the Heap.

A. Triple Equals (Strict Equality: ===)

  • Logic: No Type Coercion. It checks if both the Data Type and the Memory Address are identical.
  • Result: If the types differ, it returns false immediately. If both are objects, it checks if they occupy the same Heap slot.

B. Double Equals (Abstract Equality: ==)

  • Object vs. Object: It behaves exactly like ===. It compares addresses. No content checking occurs.
  • Object vs. Primitive: The engine triggers the ToPrimitive algorithm. It attempts to convert the object to a primitive (via .toString() or .valueOf()) to compare values instead of addresses.
const arr = [42];
const num = 42;
 
// Scenario 1: Object vs Object
console.log([42] == [42]); // false (Unique addresses in Heap)
 
// Scenario 2: Object vs Primitive (Coercion)
// arr.toString() becomes "42", then coerced to Number 42
console.log(arr == num); // true

Aliasing & Mutation

Aliasing is dangerous because it might lead to unintended result. We can modify a variable in File A, and it accidentally updates a variable in File B because they share the same Heap Address.

A. Mutation vs. Reassignment

  1. Mutation (Modifying the Heap):

    • We follow the pointer to the Heap and change the data inside.
    • Effect: All aliases see the change.
    const user = { name: "Singh" };
    user.name = "Astra"; // Allowed! The Stack pointer didn't change.
  2. Reassignment (Modifying the Stack):

    • We change the value in the Stack Slot to point to a completely different object (or value).
    • Effect: The link between the variables is broken.
    let user = { name: "Singh" };
    user = { name: "New" }; // The old link is gone.

B. The const Myth

Many developers believe const makes an object immutable. It does not.

  • Reality: const creates an Immutable Binding. It locks the Stack Slot. We cannot Reassign the variable to a new address.
  • Loophole: It does not lock the Heap. We are free to Mutate the contents of the object referenced by that slot.

Copying Strategies

Since assignment (=) only creates an alias, how do we actually clone an object?

A. Shallow Copy (The Spread Operator ...)

ES6 introduced the Spread syntax.

  • Mechanism: It creates a New Object in the Heap and iterates over the properties of the old object.
  • The Assignment Trap: For each property, it performs a standard Assignment.
    • If the property is a Primitive, it copies the value.
    • If the property is a Nested Object, it copies the Memory Address of the nested object.
  • The Limit: It only copies the First Layer. If the object contains nested objects (e.g., user.address.city), those nested objects are still copied by reference.
let original = {
  name: "Singh",
  meta: { role: "Admin" },
};
 
let copy = { ...original };
 
copy.name = "Clone"; // Safe (Original is still "Singh")
copy.meta.role = "User"; // DANGER! Original.meta.role becomes "User"

B. Deep Copy (Breaking the Chain)

To fully clone a nested tree, we need a Deep Copy.

  1. The JSON Hack (Legacy):

    • JSON.parse(JSON.stringify(obj))
    • Mechanism: Serializes the Heap graph into a flat String, then parses it back into a brand new set of objects.
    • Key Removal (Data Loss): If a value is a Functionor undefined, the engine ignores the entire key-value pair,it is removed from the string.
    • Date Transformation: It converts Date objects into ISO Strings (e.g., "2025-12-19T...").
    • The Consequence: Once converted to a string, it is no longer an object. We lose all Date methods like .getFullYear() or .setHours().
  2. structuredClone() (Modern Standard):

    • A built-in browser API available since 2022.
    • Mechanism: It uses a Recursive Algorithm to walk the entire object tree, creating new copies of every node it encounters.
    • Pros: It handles nested structures, Circular References, Dates, Maps, and Sets correctly.
    • Cons: Slightly slower than shallow copying due to the cost of recursion.
let deepCopy = structuredClone(original);
// Completely safe. No shared references.

📝 Summary Table

OperationSyntaxStack ActionHeap ActionResult
Assignmentb = aCopy SlotNoneAlias (Shared Ref)
Reassignmenta = {}Update SlotCreate new ObjBroken Link
Mutationa.x = 1NoneUpdate ObjSide Effect
Shallow Copy{...a}Create new RefCopy Layer 1Mixed (Shared Nested)
Deep CopystructuredCloneCreate new RefRecursive CopyTrue Clone