Inside the JavaScript Memory Box: Visualizing Variables, References, and Copies

There is a difference in the way JavaScript refences variables based on whether they are primitive or non-primitive

Primitive Types: string, number, bigint, Boolean, symbol, null, undefined

Non-Primitive: Arrays, Objects

Each variable in JS is stored in a memory “box”

Assignments:

But when another variable tries to assign this value, JS thinks
“wait, is this a primitive or non-primitive? Because if it is primitive, I will create a new box for this new variable with the same content. But if it’s not, I will just create a refence to the existing box”


PRIMITIVE:

Analogy: Let's say you have a note that says 5. You give a copy of this note to your friend. 
Friend now updates this copy to 10. But your copy still says 5

let a = 5;
let b = a;
b = 10; //a remains unchanged

NON-PRIMITIVE:

Analogy: Let's say you have a note with address "123 Elm Street". In that house lives a cat named "Tom". You give a copy of this note to your friend. Friend now goes to this house and re-names the cat to "Jerry".
When you go to this house, you will now find a cat named "Jerry" and not "Tom"

let a = { foo: "bar" };
let b = a;

b.foo = "baz"; //now a is also updated to { foo: "baz" }

But what if b didn't change the property but re-assigned itself a new object?

let a = { foo: "bar" }
let b = a;
b = { foo: "baz" }; //its a complete re-assignment so b has its own memory box. So a is not updated.

Using fancy words: "Primitive values are copied directly. Objects are copied by sharing a reference to the same underlying data."

Shallow Copy and Deep Copy

SHALLOW COPY

Also means using a spread operator.




let a = {
  name: "tom",

  address: {

    type: "apartment",

    unit: 3806

  }

}                                       

Level 1 : Has primitive
Level 2: Has non-primitive

let b = { ...a } 
When shallow copy is done using spread operator, level 1 is primitive type, hence b has its own copy (not shared)
But level 2 is non-primitive, it is passed by refence (shared)



Example: 
let a = {

  name: "tom",

  address: {

    type: "apartment",

    unit: 3806

  }

}
let b = { ...a }
b.name = "Jerry"; //
ONLY b changes (primitive at level 1)

b.address.type = "office"; // BOTH a and b change (shared object at level 2)

Final values of a and b

a

b

{

  name: "Jerry",

  address: {

    type: "office" //changed

    unit: 3806

  }

}

{

  name: "Jerry",

  address: {

    type: "office"

    unit: 3806

  }

}

Same with arrays

Example 1:

let a = [ 1,2,3]; 

let b = [...a]; //---> b gets its own copy, nothing is shared

b[1] = "changed"

console.log(a) // [1, 2, 3] no change

console.log(b) //[1, 'changed', 3]


Example 2:

let a = [ 1,
  [2,3,4]
]
let b = [...a];
b[0] = "primitive changed"
b[1][0] = "non-primitive changed"

console.log(b) // ["primitive changed", ["non-primitive changed", 3, 4]]
console.log(a) // [1, ["non-primitive changed", 3, 4]]

DEEP COPY

Deep copy (using structuredClone()) creates a completely independent clone of the original object.

Nothing is shared. It recursively copies every nested level, so a and b share no references.
Updating any property inside b does not affect a.

Example:

let a = {

  name: "tom",

  address: {

    type: "home",

    building: {

      name: "concorde",

      unitDetails: {

        unitNum: 2029,

        unitFloor: 20

      },

    }

  }

};

let b = structuredClone(a); 

// Mutations to b (even deeply nested) won't affect a

b.address.type = "office"; 

b.address.building.name = "Wildstone";

b.name = "jerry";

Final values of a and b

a

b

{

  "name": "tom",

  "address": {

    "type": "home",

    "building": {

      "name": "concorde",

      "unitDetails": {

        "unitNum": 2029,

        "unitFloor": 20

      }

    }

  }

}

{

  "name": "jerry",

  "address": {

    "type": "office",

    "building": {

      "name": "Wildstone",

      "unitDetails": {

        "unitNum": 2029,

        "unitFloor": 20

      }

    }

  }

}


Same with Arrays.
They hold their own copies and nothing is shared
Example:

let a = [

  { name: "tom" },

  { name: "jerry" }

];

let b = structuredClone(a);

b[0].name = "changed";

console.log(a[0].name); // "tom" (unchanged)

console.log(b[0].name); // "changed"


Type

Behavior

What’s Shared

Example

Shallow copy

Copies only the first level

Nested objects are shared

{ ...a }, Object.assign()

Deep copy

Recursively copies all levels

Nothing shared

structuredClone(a)

Comments

Popular posts from this blog

React & State Management: All Concepts

React: Communication between components