DEV Community

Cover image for JavaScript-30-Day-14
KUMAR HARSH
KUMAR HARSH

Posted on • Updated on

JavaScript-30-Day-14

JavaScript References VS Copying

demo

ss

On Day-14 of JavaScript-30 we discussed a very important concept of JavaScript or any programming language for that matter, the difference between and a reference and copy.

We'll start with:

Strings, Numbers and Booleans

let's say we have a variable age=100, now we create another variable age2=age, now if we console.log() them we'll see they have the same values in them.

Now what happens if we do age=200, now if we try console.log() them we'll see value of age is changed but but the value of age2 is unchanged.

I have provided the output in comments.

let age = 100;
      let age2 = age;
      console.log(age, age2); // 100 100
      age = 200;
      console.log(age, age2); // 200 100
Enter fullscreen mode Exit fullscreen mode

This tells us when we created age2 using the age then a separate copy was made for age2, hence when age was changed we do not see the effects of those changes on age2.

The same happens with Strings:

let name = "harsh";
      let name2 = name;
      console.log(name, name2); // harsh harsh
      name = "kumar";
      console.log(name, name2); // kumar harsh
Enter fullscreen mode Exit fullscreen mode

So in the case of Strings, Numbers and Booleans, while creating new copies of them a separate copy is created and we can make changes in one without affecting the other one.

Arrays

Let's say we have an array

const players = ["Wes", "Sarah", "Ryan", "Poppy"];
Enter fullscreen mode Exit fullscreen mode

and we want to make a copy of it.

You might think we can just do something like this:

const team = players;
Enter fullscreen mode Exit fullscreen mode

We print them and see:

console.log(players, team);
// ["Wes", "Sarah", "Ryan", "Poppy"]
// ["Wes", "Sarah", "Ryan", "Poppy"]
Enter fullscreen mode Exit fullscreen mode

We can see that both arrays contain same elements.

However what happens when we update that array?

team[3] = "Lux";
Enter fullscreen mode Exit fullscreen mode

Now here is the problem! When we print them we see:

console.log(players, team);
// ["Wes", "Sarah", "Ryan", "Lux"]
// ["Wes", "Sarah", "Ryan", "Lux"]
Enter fullscreen mode Exit fullscreen mode

We see that we have edited the original array too!

Why? It's because team isn't another array, it is just a reference to the original Array.

So we see when we try to copy arrays we get what is an array reference, not an array copy.In reality they both point to the same array!

So if we try to make changes in one of the arrays that change will be reflected in the other one.

So, how do we fix this? We take a copy instead!

And how do we do that? Well there are multiple ways to do that and we'll see a bunch of them.

slice

      const team2 = players.slice();
Enter fullscreen mode Exit fullscreen mode

If you pass nothing to splice it will simply return a copy of the original array.

concat

const team3 = [].concat(players);
Enter fullscreen mode Exit fullscreen mode

What we are doing here is take an empty array and concatenate the old one with it thus getting the same elements as the original array.

Array.from

const team5 = Array.from(players);
Enter fullscreen mode Exit fullscreen mode

ES6 spread

const teams4 = [...players];
Enter fullscreen mode Exit fullscreen mode

This is the latest and the easiest method of creating copies of array.

spread takes every item out of a iterable and puts it into the container, here an array.

You can read more about spread operator on MDN

Using any of the above methods we can create copies of arrays and now when we update it, the original one isn't changed.

Objects

The same thing goes for objects, let's say we have a person object

      const person = {
        name: "Wes Bos",
        age: 80,
      };
Enter fullscreen mode Exit fullscreen mode

and think we make a copy, but instead we get a reference and making changes would affect both.

const captain = person; //captian is just a reference
captain.number = 100; // changes person as well
Enter fullscreen mode Exit fullscreen mode

Again what we do is we take a copy instead using Object.assign()

const cap2 = Object.assign({}, person, { number: 99 });
Enter fullscreen mode Exit fullscreen mode

Object.assign() takes 3 arguments, first one is an empty object, second we pass it the object we wish to copy all the properties from and an optional third argument where we pass our own properties we would like to add to the object or existing properties whose value we would like to update, and this won't affect the original object.

1

We can also use the spread operator like arrays.

const cap3 = { ...person };
Enter fullscreen mode Exit fullscreen mode

Things to note - this is only 1 level deep - both for Arrays and Objects. lodash has a cloneDeep method, but you should think twice before using it.

So what do I mean by 1 level deep, we saw when we changed the name property the original object was unchanged, but what if try to change social property which itself is an object.

Take a loot at this:

const harsh = {
        name: "harsh",
        age: 20,
        social: {
          twitter: "@harsh",
          fb: "harsh",
        },
      };

const newObject = Object.assign({}, harsh);
Enter fullscreen mode Exit fullscreen mode

2

So we see that a change in the fb property inside social affected the original object as well. And this is why we say that all this techniques of copying are shallow copy as they work only up to one level, beyond that we need a deep copy which is a bit complicated.

We can use a shortcut though but it isn't recommended as behaviour can be unexpected.

What we can do is use:

const newObject2 = JSON.parse(JSON.stringify(harsh));
Enter fullscreen mode Exit fullscreen mode

and you can see the results:

3

We can see that the change is the social property will not affect the original object now.

Why? you may think

Because if we pass an object to JSON.stringify like JSON.stringify(harsh) then it simply converts it into a string, it is no longer an object, but then we immediately JSON.parse it like JSON.parse(JSON.stringify(harsh)) which turns it back into an Object and thus we get a full copy of the original object without any issue of references.

and with this our project for the day was completed.

GitHub repo:

Blog on Day-13 of javascript30

Blog on Day-12 of javascript30

Blog on Day-11 of javascript30

Follow me on Twitter
Follow me on Linkedin

DEV Profile

cenacr007_harsh image

You can also do the challenge at javascript30

Thanks @wesbos , WesBos to share this with us! πŸ˜ŠπŸ’–

Please comment and let me know your views

Thank You!

Oldest comments (13)

Collapse
 
rohitk570 profile image
ROHIT KUMAR

great

Collapse
 
cenacr007_harsh profile image
KUMAR HARSH

thanks

Collapse
 
rohitk570 profile image
ROHIT KUMAR

Thanks this would helps other enlightening some core concept of programming

Collapse
 
cenacr007_harsh profile image
KUMAR HARSH

Welcome

Collapse
 
tamsrua profile image
tamsrua

Thank you for the article

Collapse
 
peerreynders profile image
peerreynders • Edited

This tells us when we created age2 using the age then a separate copy was made for age2

Not exactly. Compare:

const age = 100;
let age2 = age;
age = 200;
Enter fullscreen mode Exit fullscreen mode

with

const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
let team = players;
team = ['Wes', 'Sarah', 'Lux', 'Poppy'];
Enter fullscreen mode Exit fullscreen mode

and consider JavaScript data types and data structures:

All types except objects define immutable values (that is, values which can't be changed).

So age has a reference to the immutable 100 value. After age2 = age both have an identical reference to that 100 value‑. After age2 = 200 the name age2 has a reference to the new (immutable) 200 value. So the separate copy doesn't refer to the 100 value but the reference to the same 100 value. Initially both age and age2 have a reference to the same 100 value - just like team and players initially reference the same ['Wes', 'Sarah', 'Ryan', 'Poppy'] array.

Once you understand that, const makes perfect sense. const makes the reference to the value (not the value itself) immutable.

Primitive values are already immutable. So once what age can reference cannot be changed - nothing can be changed as 100 is already immutable.
Similarly objects (and arrays) don't hold values but only references to values [1]. const players simply means that we cannot change the reference to that particular array - but it has no effect on the references stored inside the array.

[1] There is a TC39 stage 2 proposal - Record & Tuple - where values are stored directly inside tuples (#[1,2,3,4]) and records (#{x: 1, y: 2}).


‑ Update: What actually goes on is entirely up to the JavaScript engine though there is an interesting historical perspective:

The first JavaScript engine represented JavaScript values as 32-bit words. The lowest 3 bits of such a word were used as a type tag, to indicate whether the value was an object, an integer, a double, a string, or a boolean (as you can see, even this early engine already stored numbers as integers if possible).

The type tag for objects was 000. In order to represent the value null, the engine used the machine language NULL pointer, a word where all bits are zero. typeof checked the type tag to determine the type of value, which is why it reported null to be an object.

With that in mind

This tells us when we created age2 using the age then a separate copy was made for age2

is correct. But what needs to be observed is that regardless whether age is primitive or non-primitive the same thing is "copied":

  • for a primitive value that thing is the value
  • for a non-primitive that thing is the reference to the non-primitive value

This leads to:

All primitives are immutable, i.e., they cannot be altered. It is important not to confuse a primitive itself with a variable assigned a primitive value. The variable may be reassigned a new value, but the existing value can not be changed in the ways that objects, arrays, and functions can be altered.

From a historical perspective it seems to be perfectly reasonable to be able to overwrite an "immutable primitive value" wholesale in order to reassign the variable.

Collapse
 
cenacr007_harsh profile image
KUMAR HARSH

Thanks for pointing this out and the detailed explanation, the instructor never mentioned it maybe he wanted to keep things simple and that is how it works in other languages like C++ so it never occurred to me to get into depth.

Collapse
 
peerreynders profile image
peerreynders • Edited

that is how it works in other languages like C++

In C++ :

  int n = 5;    // this declares a variable, n 
  int & r = n;  // this declares r as a reference to n 
Enter fullscreen mode Exit fullscreen mode

i.e. n is bound to one specific memory location but the value inside that location can be changed. r is an alias that refers to exactly the same memory location.

Now from const:

The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutableβ€”just that the variable identifier cannot be reassigned.

This is where it leaks through that JavaScript doesn't have "variables" in the sense of

  • the name that refers to one and only one memory location which contains some value that can change

but instead implements

  • the name that refers to one and only one memory location which contains a reference to the value which the name is bound to at this point in time

maybe he wanted to keep things simple.

Many learning materials do this because they begin with primitive values and it is felt that the extra indirection introduced by references is just going to confuse the beginning student. Which makes sense but this shapes the incorrect mental model.
By the time arrays and objects are introduced the notion of a reference needs to be understood anyway - and it is at this point that the mental model regarding variables and primitive values needs to be adjusted - which rarely happens and which leads to confusion later.

As const demonstrates - variable( reference)s behave exactly the same for primitive values and objects because assignment doesn't change the value but changes which value is bound to the name.

Thread Thread
 
cenacr007_harsh profile image
KUMAR HARSH

"This is where it leaks through that JavaScript doesn't have "variables" in the sense of

the name that refers to one and only one memory location which contains some value that can change
but instead implements

the name that refers to one and only one memory location which contains a reference to the value which the name is bound to at this point in time"

I would like to understand this in more detail, about how variables work, can you point me to a source where I can learn about this in greater depth.

Thread Thread
 
peerreynders profile image
peerreynders

Look, there are lots of sources that will say that I'm wrong (and perhaps I am) - for example:

When assigning a primitive value to a variable or passing it as an argument to a function, its content is copied.

While this superficially works as a mental model (especially if you are trying to avoid talking about references) it makes no sense if all primitive values are immutable - duplicating immutable values is simply wasteful.

From What Does it Mean that Primitive Values are Immutable in JavaScript?:

Variables just point to values.

The "content is copied" mindset fosters the view that "variables point to the same memory address for their entire lifetime" - for example:

let x = 123;

// Content for variable `x` will always 
// live at address `#a` so:
// #a: 123

let y = x;
// Content for variable `y` will always 
// live at address `#b` 
// and content is copied, so:
// #a: 123
// #b: 123

y += 1;
// We only change the content for `y`, so:
// #a: 123
// #b: 124
//
// Note that the value at `#b` changed. 
// This violates the assertion 
// that primitive values are immutable.
Enter fullscreen mode Exit fullscreen mode

Compare that to the "variables just point to values" mindset:

let x = 123;

// Create a new immutable value at memory location `#a` 
// and have `x` point to it: 
// #a: 123
// x: #a

let y = x;
// `y` needs to point to the same value as `x` 
// so just _copy the reference_:
// #a: 123
// x: #a
// y: #a

y += 1;
// Primitive values are immutable 
// so we create the new value `124` 
// and store it at memory location `#b` 
// and have `y` refer to it: 
// #a: 123
// #b: 124
// x: #a
// y: #b
Enter fullscreen mode Exit fullscreen mode

The thing is the "variables just point to values" view simplifies everything because now variables work exactly the same for primitive and non-primitive values and it also explains the behaviour of const - const makes the reference used by the variable immutable, so you're stuck with the same primitive or non-primitive value for the lifetime of that variable; however that doesn't affect the internal mutability of non-primitive values.

Thread Thread
 
cenacr007_harsh profile image
KUMAR HARSH

That was some great explanation, thanks for taking out the time to explain it so clearly.

Collapse
 
cenacr007_harsh profile image
KUMAR HARSH • Edited

After learning React and Redux and about State immutability I finally understand this immutability concept.

Collapse
 
rash123 profile image
RASHMI VERMA

πŸ‘