I think it's important to understand memory and what goes on when you declare variables. Memory inside of a computer can be a confusing and abstract thing for a human mind to understand, so I think the best way to come to terms with it is through an analogy, which I will use as I explain this.
Imagine that your computer's memory is a warehouse. Inside of that warehouse there are storage bins where boxes of data are kept. Upon declaring a variable, you ship a box to that warehouse where it is then given a bin that will hold it, until you need it later.
Primitive data types in Javascript are Passed by Value. If you're not sure what the seven primitive data types are, that's okay. I would stop and read this to get an idea. Chances are you've seen them all while you've been learning, but knowing the difference helps here. MDN: Javascript - Data Structures.
So say you set a variable to equal another. For example:
let box1 = 'sugar'
let box2 = box1
Let's break this down... in our proverbial warehouse one of the workers goes to the bin box1
is in, examines the box, and uses its Javascript magic to create an exact clone of it. The worker then carries the clone off and stores it in a new bin for box2
.
The value is copied, you see, box1
and box2
both have 'sugar
'.
So, what if we change one of the values?
box2 = 'brown sugar'
console.log(box1) // returns 'sugar'
console.log(box2) // returns 'brown sugar'
They're no longer the same, but that's okay. Only the original value was passed when box2
was created, they're not related to each other in any way and thus have no effect on each other.
Objects in Javascript use Pass by Reference. Most of the constructs in Javascript we use are Objects, so I think it's important to understand how this works. Objects constitute {}
with key-value pairs, as well as things like arrays and functions. You've probably heard the saying that "everything in Javascript is an object." It's somewhat true!
const box3 = {
contents: "salt"
}
const box4 = box3
In this example our little worker recognizes that box3
is an Object. So he scribbles down its location in the warehouse. It then zips off to an open container for box4
and tapes the paper on the rack, referencing the location of box3
and its contents.
That is Passed by Reference. Sometimes the Objects we create or pull into our apps can be massive, with hundreds or perhaps even thousands of key-value pairs. It would be incredibly wasteful and not performant of the computer to make a clone everytime.
So, it simply references instead. At times this can have unforeseen consequences.
box4.contents = "pepper"
console.log(box3.contents) //returns "pepper"
console.log(box4.contents) //returns "pepper"
Wait, hold on! We didn't mean for that to happen. Why did it?
Because box4
doesn't contain its own value, it contains a reference to box3
. By updating the contains
property on box4
, we're actually telling it to update box3.contains
.
That right there is where the difference can come to bite us. So, the question is, how do we make a clone of box3
, rather than passing the reference along?
Well, ES6 gave us a very clean and nice way to do it, the spread operator.
box4 = { ...box3 }
You can also use the trusty old method of cloning, if you wish.
box4 = Object.assign({}, box3)
Mind you, this is a very basic primer to how these things work. I hope my examples and warehouse analogy helped some of you imagine the difference a little better than just reading a definition. Play around with this, experiment. Also, dig deeper, as it's a very important subject for working with data in your apps.
You'll cross paths with it or brush up against it at some point, I guarantee it.
EDIT: I found out from a really informative comment that this is a bit more nuanced than first appears, for more information check out this post: https://dev.to/xpbytes/javascript-ruby-and-c-are-not-call-by-reference-23f7
Top comments (17)
Whilst this is a great post and the overal information is correct, the pedantic semantic details are not. It all comes down to what it means to say something is "pass by x", and not what people who ask this question usually meant. I will explain why, but first
JavaScript is always pass by value
You're not the first one that tries to make a distinction between primitives and objects, and I understand why people do. However, there is none.
The term
pass by reference
andpass by value
only applies to function calls and their arguments. Consider the following (JS syntax) code:The fact is that
someObject
has not been changed, because it was not areference
tosomeObject
's content that has been passed. A language that does supportpass by reference
is PHP, but it requires special syntax to change from the default of passing by value:I tried to keep the same sort of semantic as the JS code.
As you can see, the PHP example actually changes the value the input argument refers to.
What's wrong with call-by-value?
The term
call-by-value
is problematic. In JavaScript the value that is passed is a reference. That means that, indeed, that the reference to the boxed primitive is copied, and therefore changing a primitive inside a function doesn't affect the primitive on the outside. That also means that, indeed, the reference to a reference type, such as an array or object, is copied and passed as the value. This results in the exact behaviour that is described.The lesser used and known term that was coined is Call by sharing which applies to Ruby, JavaScript, Python, Java and so forth. It implies that all values are object, all values are boxed, and they copy a reference when they pass it as value.
Here is another example to demonstrate this:
In the first example it outputs
[1]
, because thepush
method modifies the object on which it is called. This propagates because thelist
argument still refers to the original objectfirst
(its reference was copied and passed as a value).In the second example it outputs
[]
because the re-assignment doesn't propagate to the caller. In the end it is not re-assigning the original reference but only a copy.In short: It's always pass by value, but the value of the variable is a reference. All primitive-methods return a new value and thus one can not modify it, all objects and arrays can have methods that modified their value, and thus one can modify it.
P.S. C actually is also always pass by value / call by value, but it allows you to pass a pointer which can simulate pass by reference:
Wow, the resources I used to learn Javascript never mentioned this at all, thanks for sharing! Very illuminating.
If you try to look this up, most resources will actually mention it incorrectly and even hostile toxic discussion against people who say exactly what I say :(. I only know what I know because I spend quite some time in this implementation in V8 and MRI ruby (and because I had some of this in university).
It also doesn't help that it's really mostly semantics and pedantic, but nomenclature can definitely confusing. I hope you can turn my explanation in a few sentences for the #beginners β€
This exactly. Almost no resource explains this correctly and I think that this is one of the harder things to explain to beginners, but also one of the most important. Obviously, someone who had some C/C++ education would be a bit better at understanding the reasons why this happens, but even those lessons are usually not very good at explaining the differences in detail.
Your explanation on the other hand is pretty good. And good job! :)
I guess I'll write an article.
That said, I completely understand where Brian is coming from. It's moreso the lack of writing than that it's super hard. I think Brian did a stellar job giving us the difference of passing an object/array vs primitive, in the practical sense, which is in the end the only thing the beginner will really use.
Thank you! I see the misconception about pass by reference / value a lot.
This comment should probably be it's own blog post.
I will!
Yeah I do apologize for potentially spreading misinformation. I was just recapping things I learned in an Advanced Javascript course.
Link me to your post when you make it, and I'll add it at the bottom of this one as a "However, this is more nuanced than it seems on the surface, for a deeper dive check out this article."
Don't worry! Keep recapping. Like I said, it holds value :)
I'll reply here with the link once it's up!
JavaScript, Ruby and C are not call by reference
Derk-Jan Karrenbeld γ» Jun 27 γ» 9 min read
Don't forget about differences between shallow and deep copies...
Both mentioned methods create only shallow copy of object.
Very true! I didn't want to get to deep into that as it may add confusion, but it's a good thing to look into next for sure!
This is a great post, it brings up a deeply complex topic even for those who have written javascript for a long time. The comments in the thread area also eye opening. Great job being a conversation starter and sharing with analogies. Great post! Keep em up. I love reading them.
Something doesn't feel right about this post. This is a GC language I shouldn't have such concerns. Although I love that you have taken the time to write about it.
I'm sorry.
Dont be :D