Value Types vs Reference types in JavaScript Explained

chowjiaming

Joseph Chow

Posted on January 18, 2021

Value Types vs Reference types in JavaScript Explained

When storing a variable in JavaScript, the JavaScript engine may store it as one of two types of values: a primitive or reference value. Understanding the difference in how they behave will help to avoid mistakes while manipulating them.

An Example

Instead of jumping straight into a boring explaination, consider this script:

const firstList = ['A', 'B', 'C'];
const secondList = firstList;
secondList.push('D');
console.log('firstList:', firstList);
console.log('secondList:', secondList);
Enter fullscreen mode Exit fullscreen mode

We may expect the output to be:

"firstList:" ['A', 'B', 'C'];
"secondList:" ['A', 'B', 'C', 'D'];
Enter fullscreen mode Exit fullscreen mode

But instead, we get an output of:

"firstList:" ['A', 'B', 'C', 'D'];
"secondList:" ['A', 'B', 'C', 'D'];
Enter fullscreen mode Exit fullscreen mode

What Happened

This is because of how JavaScript treats arrays in memory. Arrays are stored as a reference value, so JavaScript will only copy the reference to that point in memory. This means that to the original array and not the value of the array.

Diving deeper, when accessing the varible as a primitive value, you are manipulating the actual value stored of that variable. In other words, the variable that is assigned a primitive value is accessed by value.

Unlike storing a primitive value, when you create an object, you are accessing the reference of that object in memory, rather than the actual value of that object. In other words, it means a variable that is assigned an object is accessed by reference.

Fixing our Example

The spread operator will create a new reference value in memory.

const firstList = ['A', 'B', 'C'];
const secondList = [...firstList];
secondList.push('D');
console.log('firstList:', firstList); // "firstList:" ['A', 'B', 'C'];
console.log('secondList:', secondList); // "secondList:" ['A', 'B', 'C', 'D'];
Enter fullscreen mode Exit fullscreen mode

In ES5, you could copy an array into a new reference value with the slice method.

const firstList = ['A', 'B', 'C'];
const secondList = firstList.slice();
secondList.push('D');
console.log('firstList:', firstList); // "firstList:" ['A', 'B', 'C'];
console.log('secondList:', secondList); // "secondList:" ['A', 'B', 'C', 'D'];
Enter fullscreen mode Exit fullscreen mode

However, the slice method cannot be used to create sparse arrays or arrays with 'holes' in them.

let array = [];
array[2] = 2;
array[4] = 4;

console.log(array.slice()); // [empty × 2, 2, empty, 4]
console.log(...array); // undefined undefined 2 undefined 4

Enter fullscreen mode Exit fullscreen mode

Diving Deeper

Javascript has six data types that values are assigned as primative type: Boolean, null, undefined, String, Number, and Symbol. Like mentioned above, the values in primative types are accessed only by value, so they can be copied by value. The values can be copied and changed with no relation to each other.

let a = true;
let b = 'hello';
let x = a;
let y = b;
console.log(x, y, a, b); // true, 'hello', true, 'hello'
x = null;
y = undefined;
console.log(x, y, a, b); // null, undefined, true, 'hello'
Enter fullscreen mode Exit fullscreen mode

Arrays, Functions, and Objects are all derived from object contructors in JavaScript. An explaination for this can be found here. The important thing to understand is that objects are passed by reference value. Like mentioned above, a varible assigned an object only knows is the location of the object in memory, not the object itself. Here is an example of a common bug that occurs when working with objects:

let vehicleOne = {
  seats: 2,
  airConditioning: true
};
let vehicleTwo = vehicleOne;
vehicleOne.color = 'red';
console.log(vehicleOne.color); // 'red'
console.log(vehicleTwo.color); // 'red'

vehicleTwo = {...vehicleOne};
vehicleOne.color = 'white';
console.log(vehicleOne.color); // 'white'
console.log(vehicleTwo.color); // 'red'
Enter fullscreen mode Exit fullscreen mode

By using the '=' operator, we only assigned vehicleTwo the reference to the object, so any changes made to the vehicleOne object will alter the same spot in memory that vehicleTwo is assigned. Once again, we used the spread operator to copy and create a new reference value for vehicleTwo to avoid this error. This is why objects are known to be mutable in JavaScript.

This is also why you need to treat state in React as immutable, because the reference value will not change if you try and manipulate the state directly. You can read more about treating state as being immutable in React here.

Deep Copying

Both times we used the spread operator, it managed to create us a reference value for a new object. However, there is a catch. Consider this script:

const bookOne = {
  title: 'Primative Types vs Reference Values in JavaScript',
  pages: 50,
  info: {
    name: 'Joseph',
    publication: 'dev.to'
  }
}

const bookTwo = { ...bookOne }


bookTwo.title = 'Immutability in JavaScript';
bookTwo.info.publication = 'medium.com';

console.log('Book One title:', bookOne.title); // 'Primative Types vs Reference Values in JavaScript'
console.log('Book One authors:', bookOne.info.publication); // 'dev.to'

console.log('Book Two:', bookTwo.title); // 'Immutability in JavaScript'
console.log('Book Two:', bookTwo.info.publication); // 'dev.to'
Enter fullscreen mode Exit fullscreen mode

Only the title of bookTwo changed. If one of the elements is another reference value to another object, all it would do is make a copy of the reference value into memory and it will not change what it’s referenced to. Here is a great article for further reading into how to deep copy objects in JavaScript.

💖 💪 🙅 🚩
chowjiaming
Joseph Chow

Posted on January 18, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related