Fun Programming

How to Handle Unexpected Changes in JavaScript Objects




When Your Object Has a Mind of Its Own

Have you ever faced a weird moment while coding?

You created an object in JavaScript, set the values, but suddenly — boom! — something changed. You didn’t touch it (at least, not that you remember), but somehow the data's different.


let user = { name: "John" };
let copy = user;

copy.name = "Alice";
console.log(user.name); // "Alice" ???

Yep, both user and copy point to the same object!

 

Why Does This Happen?

In JavaScript, objects (including arrays and functions) are reference types.

This means when you assign an object to a variable, you're assigning a reference to it — not a copy of the object.

It’s like giving someone your house address. If they repaint your house, you’ll see the new color too. Because it’s still the same house.


let user = { name: "John" };
let clone = user;

clone.name = "Mark";

console.log(user.name); // Mark

 

Primitive vs Reference Types

Primitive Reference
Number, String, Boolean, Null, Undefined, Symbol, BigInt Object, Array, Function

// Primitive
let a = 10;
let b = a;
b = 20;

console.log(a); // 10

// Reference
let obj1 = { num: 10 };
let obj2 = obj1;
obj2.num = 99;

console.log(obj1.num); // 99

 

Why It’s Dangerous

  • You might unintentionally modify original data
  • Changes in one place can affect others
  • State becomes hard to track
  • Can cause bugs in frameworks like React/Vue

 

How to Avoid Unexpected Mutations

 

1. Use Shallow Copy

Use Object.assign() or the spread operator ... for shallow copies:


let user = { name: "Jane", age: 25 };
let copy = { ...user };

copy.name = "Lucy";
console.log(user.name); // Jane 

But beware of nested objects:


let user = {
  name: "Alex",
  address: { city: "Jakarta" }
};

let copy = { ...user };
copy.address.city = "Bandung";

console.log(user.address.city); // Bandung 

Because nested properties are still references.

2. Deep Copy


let deepCopy = JSON.parse(JSON.stringify(user));

This creates a new object entirely — but has limitations:

  • Doesn't work with functions, Dates, RegExps
  • Fails with circular references

3. Use Libraries for Deep Clone

Try tools like:

  • Lodash_.cloneDeep(obj)
  • Ramda
  • Immer

import _ from 'lodash';
let copy = _.cloneDeep(user);

4. Freeze the Object

Prevent mutations using Object.freeze():


let user = Object.freeze({
  name: "Budi"
});

user.name = "Joko"; // silently ignored

console.log(user.name); // Budi

Note: Freezing is shallow — nested objects are still mutable.

5. Use Immutable Patterns


let newUser = {
  ...user,
  name: "Dina"
};

This is standard in modern frameworks like React or Redux.

 

Avoid Direct Mutation


// Bad
function updateUser(user) {
  user.name = "Ani";
  return user;
}

// Better
function updateUser(user) {
  return {
    ...user,
    name: "Ani"
  };
}

 

Common Bug Examples

 

1. React Component Doesn’t Re-render


state.user.name = "Joko"; // modifies state directly

// React doesn’t notice the change

 Fix:


setState({ user: { ...state.user, name: "Joko" } });

2. API Data Modified Unexpectedly


const data = await fetchData();
let copy = data;

copy.items[0].name = "X";

console.log(data.items[0].name); // "X" 

 Fix:


let safeCopy = JSON.parse(JSON.stringify(data));
Or:

let safeCopy = _.cloneDeep(data);

 

Best Practices Checklist

  •  Use { ...obj } or Object.assign() for shallow copies
  •  Use _.cloneDeep() or JSON deep copy for full clones
  •  Never mutate props/state/API data directly
  •  Use immutable update patterns
  •  Remember arrays are also objects!

 

Objects Aren’t Evil — Just Misunderstood

JavaScript objects are powerful — but if you treat them like primitives, you're gonna have a bad time.

Understanding reference behavior and making safe copies will save you from mysterious bugs, especially in complex apps.

“Respect the reference. Copy wisely. And freeze when necessary.”



No comments:

Post a Comment