DEV Community

Cover image for JS Fundamentals: Object Assignment vs. Primitive Assignment
Nick Scialli (he/him)
Nick Scialli (he/him)

Posted on • Edited on

JS Fundamentals: Object Assignment vs. Primitive Assignment

Introduction

Something I wish I had understood early on in my JavaScript programming career is how object assignment works and how it's different from primitive assignment. This is my attempt to convey the distinction in the most concise way possible!

Learn JS Fundamentals

Looking to learn more JS fundamentals? Consider signing up for my free mailing list!

Primitives vs. Objects

As a review, let's recall the different primitive types and objects in JavaScript.

Primitive types: Boolean, Null, Undefined, Number, BigInt (you probably won't see this much), String, Symbol (you probably won't see this much)

Object types: Object, Array, Date, Many others

How Primitive and Object Assignment Differ

Primitive Assignment

Assigning a primitive value to a variable is fairly staightforward: the value is assigned to the variable. Let's look at an example.

const a = 'hello';
const b = a;
Enter fullscreen mode Exit fullscreen mode

In this case, a is set to the value hello and b is also set to the value hello. This means if we set b to a new value, a will remain unchanged; there is no relationship between a and b.

const b = 'foobar';
console.log(a); // "hello"
console.log(b); // "foobar"
Enter fullscreen mode Exit fullscreen mode

Object Assignment

Object assignment works differently. Assigning an object to a variable does the following:

  • Creates the object in memory
  • Assigns a reference to the object in memory to the variable

Why is this a big deal? Let's explore.

const a = { name: 'Joe' };
const b = a;
Enter fullscreen mode Exit fullscreen mode

The first line creates the object { name: 'Joe' } in memory and then assigns a reference to that object to variable a. The second line assigns a reference to that same object in memory to b!

So to answer the "why is this a big deal" question, let's mutate a property of the object assigned to b:

b.name = 'Jane';
console.log(b); // { name: "Jane" }
console.log(a); // { name: "Jane" }
Enter fullscreen mode Exit fullscreen mode

That's right! Since a and b are assigned a reference to the same object in memory, mutating a property on b is really just mutating a property on the object in memory that both a and b are pointing to.

To be thorough, we can see this in action with arrays as well.

const a = ['foo'];
const b = a;

b[0] = 'bar';

console.log(b); // ["bar"]
console.log(a); // ["bar"]
Enter fullscreen mode Exit fullscreen mode

This Applies to Function Arguments too!

These assignment rules apply when you pass objects to functions too! Check out the following example:

const a = { name: 'Joe' };

function doSomething(val) {
  val.name = 'Bip';
}

doSomething(a);
console.log(a); // { name: "Bip" }
Enter fullscreen mode Exit fullscreen mode

The moral of the story: beware of mutating objects you pass to functions unless this is intended (I don't think there are many instances you'd really want to do this).

Preventing Unintended Mutation

In a lot of cases, this behavior can be desired. Pointing to the same object in memory helps us pass references around and do clever things. However, this is not always the desired behavior, and when you start mutating objects unintentionally you can end up with some very confusing bugs.

There are a few ways to make sure your objects are unique. I'll go over some of them here, but rest assured this list will not be comprehensive.

The Spread Operator (...)

The spread operator is a great way to make a shallow copy of an object or array. Let's use it to copy an object.

const a = { name: 'Joe' };
const b = { ...a };
b.name = 'Jane';
console.log(b); // { name: "Jane" }
console.log(a); // { name: "Joe" }
Enter fullscreen mode Exit fullscreen mode

A note on "shallow" copying

It's important to understand shallow copying versus deep copying. Shallow copying works well for object that are only one level deep, but nested object become problematic. Let's use the following example:

const a = {
  name: 'Joe',
  dog: {
    name: 'Daffodil',
  },
};
const b = { ...a };

b.name = 'Pete';
b.dog.name = 'Frenchie';
console.log(a);
// {
//   name: 'Joe',
//   dog: {
//     name: 'Frenchie',
//   },
// }
Enter fullscreen mode Exit fullscreen mode

We successfully copied a one level deep, but the properties at the second level are still referencing the same objects in memory! For this reason, people have invented ways to do "deep" copying, such as using a library like deep-copy or serializing and de-serializing an object.

Using Object.assign

Object.assign can be used to create a new object based on another object. The syntax goes like this:

const a = { name: 'Joe' };
const b = Object.create({}, a);
Enter fullscreen mode Exit fullscreen mode

Beware; this is still a shallow copy!

Serialize and De-serialize

One method that can be used to deep copy an object is to serialize and de-serialize the object. One common way to do this is using JSON.stringify and JSON.parse.

const a = {
  name: 'Joe',
  dog: {
    name: 'Daffodil',
  },
};
const b = JSON.parse(JSON.stringify(a));
b.name = 'Eva';
b.dog.name = 'Jojo';
console.log(a);
// {
//   name: 'Joe',
//   dog: {
//     name: 'Daffodil',
//   },
// }

console.log(b);
// {
//   name: 'Eva',
//   dog: {
//     name: 'Jojo',
//   },
// }
Enter fullscreen mode Exit fullscreen mode

This does have its downsides though. Serializing an de-serializing doesn't preserve complex objects like functions.

A Deep Copy Library

It's fairly common to bring in a deep copy library to do the heavy lifting on this task, especially if your object has an unknown or particularly deep hierarchy. These libraries are typically functions that perform one of the aforementioned shallow copy methods recursively down the object tree.

Conclusion

While this can seem like a complex topic, you'll be just fine if you maintain awareness about how primitive types and objects are assigned differently. Play around with some of these examples and, if you're up for it, attempt writing your own deep copy function!

Top comments (5)

Collapse
 
thisdotmedia_staff profile image
This Dot Media

Great article Nick! Well-written, clear, and concise. Really appreciate the images and examples to go along with your main points. This will help a number of devs for sure! Thanks for sharing with us :)

Collapse
 
alphavader profile image
KB

Great article, thanks alot!!

Collapse
 
caketi profile image
zhao

const b = JSON.parse(JSON.serialize(a));

serialize? maybe stringify

Collapse
 
rakesh_suryawanshi profile image
Rakesh Suryawanshi

Thanks

Collapse
 
fabianaasara profile image
Fabiana Asara

Nice work 🤩easily explained! Thanks.