Things you should know about Objects and Immutability in JavaScript
Technical Opinion

Things you should know about Objects and Immutability in JavaScript

As a JavaScript developer, we should know that most things are objects – from core features, like strings and arrays, to the browser’s APIs. Here you can have a full introduction about objects in JavaScript.
JS’s objects are so beautiful: we can copy them, change and remove properties and do many cool things.

However, it’s always good to remember:

Ben Parker says to Peter Parker: With great power comes great responsibility.

What’s wrong with mutability?

That’s a question many developers have, and here a reasonable answer: there is nothing wrong if the code is mutable – the JavaScript’s Array API is and there’s nothing wrong with that. Yet, the misuse of mutability can cause side effects to your software. Here’s an example:


const user = {
  name: 'John Due',
  birthdate: '1988-08-15',
}

const changeName = (user, newName) => {
  const newUser = user
  newUser.name = newName
  return newUser
}

const userWithNewName = changeName(user, 'Jojo') 
// both users will have the name = 'Jojo'
console.log(user, userWithNewName) 

We can see that: although we are creating a new object, the user one was modified. This happens because when you assign a new value to an existing object, you are just assigning a memory reference to it.


const newUser = user

Come to the Immutable side of the force

Immutability is the art of maintaining the state of an object, making development simple, traceable, testable and decreasing any possible side effects. The main idea is: an update should not change the object, but create a new object with the updated data.

We can achieve immutability through pure functions, aka functions that will always return a given value if provided with the same input values. Neat, right? See this example:


const user = {
  name: 'John Due',
  birthdate: '1988-08-15',
}

const changeName = (user, newName) => {
  return {
    ...user, // destructuring user object
    name: newName, // override name attribute with new name
  }
}

const userWithNewName = changeName(user, 'Jojo')
// the old user object has name = 'John Due' and the new object, name = 'Jojo'
console.log(user, userWithNewName) 

Here we have a user object and its changeName function. You can see that, given the initial user and the new name, the initial object isn’t updated, and we always have the same result.

Creating Pure Functions and Removing References

Okay, we already know what can happen if we change the object’s state, now we need to know how to work with objects without causing side effects.

There are some ways to make immutable objects, but the most recommended approach to create them is using the Objects API and the ES6 destructuring assignment. We could also use functions like .map, .filter and .reduce to accomplish the same task.


// Example using Objects.assign
const changeName = (user, newName) => Object.assign({}, user, {
  name: newName
})

// Example using destructuring assignment
const changeName = (user, newName) => ({
  ...user,
  name: newName,
})

Wrapping up

When coding with JavaScript, manipulating objects is simple and practical, even though, it requires some care so your code does not end up bringing you headaches. Always remember to test your code to avoid unexpected mutation of objects.

There are some libraries that work pretty good with immutability, like ImmutableJS, Freezer and seamless-immutable.

Here at Cheesecake Labs we use Redux and ImmutableJS to accomplish pure and functional Front-end projects, keeping our code clean and easy to read.

Happy Coding!

About the author

Daniel Leite

A Front-End developer passionate about open source culture. Trying to help people by sharing codes. Creator of strman and valid.js.

Need a team for your projects?
We'd love to hear your ideas!

Connect with us!
  • Jeff M

    > We can achieve immutability through pure (idempotent) functions

    I’ve seen this repeated a lot recently, but it’s wrong. “Idempotent” is *not* a synonym for “pure”.

    https://en.wikipedia.org/wiki/Idempotence

    // Pure, yes; idempotent, yes
    timesZero = (x) => x * 0
    timesZero(7) === timesZero(timesZero(7))

    // Pure, yes; idempotent, no
    addOne = (x) => x + 1
    addOne(7) !== addOne(addOne(7))

    // Pure, no; idempotent, yes
    mutateTimesZero = (o) => o.x *= 0

    o1 = {x: 7}
    mutateTimesZero(o1)

    o2 = {x: 7}
    mutateTimesZero(o2)
    mutateTimesZero(o2)

    o1.x === o2.x

    // Pure, no; idempotent, no
    mutateAddOne = (o) => o.x += 1

    o1 = {x: 7}
    mutateAddOne(o1)

    o2 = {x: 7}
    mutateAddOne(o2)
    mutateAddOne(o2)

    o1.x !== o2.x

    —-

    EDIT: Looks like this misuse of the term originated from Eric Elliott. Not a big surprise there.

    Public Service Announcement for OP and others: Most of what Eric Elliott writes is wrong. He’s a better salesman than he is a programmer. You’re better off not learning from him.

  • Vanya Dineva

    Your sample code doesn’t seem to run. The spread operator can be applied only to iterable objects, and objects are not iterable. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator)

    • Paul Shan

      Cause object spread operators are still not a stage 4 feature and browsers still have not implemented it

  • Jochen Schmidt

    The section where you stated “although we are creating a new object” is misleading. You just create a new variable binding, initializing it using an existing object. There is no reason to think about “memory references” or not. Conceptionally _every_ thing in JavaScript (and Python, Lisp…) is a reference to an “object”. Primitive types like numbers are represented as “immediate references” following clever schemes to encode the value into the reference (tagged pointers or NaN-Boxing). What you probably meant was “copy by value”-semantics when assigning a value. You can use that in C++ or C# – structures in C# are so called “value types” which just means there will automatically made a copy on assignment. There is no such thing in JavaScript (and most other languages)