Clearing up the confusion around JavaScript references
One question that seems to come up everywhere in discussions of JavaScript is:
Is JavaScript pass-by-reference or pass-by-value?
This is often asked by people who don’t really understand what pass-by-reference or pass-by-value really mean. One of the stock answers is:
Objects are passed by reference; primitives are passed by value.
I really don’t think this is the best way to describe the situation, so I’m going to try a new way.
What is a reference? (with cats)
I’m going to explain references with cats. Don’t worry, it’ll make purrfect sense.
Let’s say you have two cats, Bloopy and Floopy. Bloopy is a boy cat and Floopy is a girl cat. One day you announce to the world:
My favourite cat is Bloopy.
Let’s say you live your life as if you were inside a JavaScript interpreter. So you declare a new variable to hold your cat:
var fav_cat = Bloopy;
Imagine that this concept of “favourite” is like an invisible wire that links you to Bloopy. As long as Bloopy is your favourite cat you’ll be linked.
You can use this link to send messages to Bloopy. Say you bought Bloopy a new collar for Christmas. If you wanted him to wear it, you could say:
fav_cat.wear_collar();
You’re sending a message down your invisible wire telling Bloopy to wear his new collar. Because you’re living inside a JavaScript interpreter that works out fine.
One day, Bloopy makes a mess on your carpet. This upsets you, to the point that you decide that Floopy is now your favourite cat:
fav_cat = Floopy;
You’ve now re-assigned your favouritism. In terms of the invisible wire, you’ve detached it from Bloopy and attached it to Floopy. You can now ask Floopy to try on the new slippers you got her for her birthday:
fav_cat.wear_slippers();
One day, Floopy does something unmentionable. You now have no favourite cat:
fav_cat = null;
Mutability
So, what does that story tell us? Well, fav_cat
is a JavaScript variable. At the beginning, it holds a reference to Bloopy. Later on, it holds a reference to Floopy. Don’t worry, nothing happened to Bloopy, but there wasn’t a reference to him stored in fav_cat
any more. At two points in the story, we called methods on fav_cat
. These methods mutated the underlying cat, in the first case to make it wear a collar, and in the second to make it wear slippers.
So, there’s two completely different things happening here; mutation and assignment. Mutation modifies the underlying object without affecting the link between the variable and the object, whereas assignment changes the link to a different object, but doesn’t modify any objects.
Immutability
Let’s continue the story. One day, you look up at the blue sky, and it inspires you to declare to the world that your favourite colour is blue:
var fav_col = "blue";
The next day you look at the grass, and declare that, in fact, this is the most beautiful colour:
fav_col = "green";
You can still think of this as there being an invisible wire between you and your favourite colour, but as colours (and JavaScript strings) are immutable, you can’t modify them using the wire - all you can do is re-assign your favourite colour to a new string.
So what does pass-by-reference mean?
In JavaScript, you never hold an object in a variable, you hold a reference to that object. It’s that invisible wire. You can send messages along the wire and you can tell the wire to attach to a different object, but that’s all. Let’s now look at passing a variable to a function:
function mutate(obj) {
obj.name = 'Mutated';
}
var my_cat = { name: 'Floopy' };
mutate(my_cat);
alert(my_cat.name);
So, what’s happening here? When my_cat
is passed into mutate
, the parameter obj
is given a reference to the same object that my_cat
contains a reference to. So now my_cat
and obj
both have an invisible wire that links them to the object named ‘Floopy’. Inside the function, the name is changed to ‘Mutated’. The object that my_cat
contained a reference to has been mutated by the function, so the last line will alert ‘Mutated’.
So, if that’s what happens with mutation, what happens with assignment? Here’s an example:
function reassign(obj) {
obj = {};
}
var my_cat = { name: 'Floopy' };
reassign(my_cat);
alert(my_cat.name);
Ok, what’s going on here? When my_cat
is passed into reassign
, both obj
and my_cat
hold an invisible wire connecting them to the object named ‘Floopy’. Inside reassign
, obj
is reassigned to an empty object. So reassign
has severed the invisible wire connecting obj
to Floopy, and reattached the wire to a new, empty object. reassign
doesn’t have any control over the link between my_cat
and its object, so my_cat.name
remains ‘Floopy’.
But what about pass-by-value?
Here’s what I say: forget what anyone ever told you about pass-by-value.
Another example:
function changeColour(col) {
col = 'green';
}
var fav_col = 'blue';
changeColour(fav_col);
alert(fav_col);
I don’t think you’re going to be surprised that fav_col
remains ‘blue’. That’s because chageColour
reassigned - it didn’t mutate. And why is this called pass-by-value when the object example was called pass-by-reference? Because strings are immutable. You can’t mutate a string inside a function so there’s no way to change fav_col
from inside changeColour
.
And finally, the conclusion (with no cats)
The point is, it doesn’t matter whether the JavaScript interpreter holds a reference to the string ‘blue’, and passes a reference into changeColour
or whether it passes the actual string into the function - either way the string can’t be mutated. The only way to change fav_col
is to reassign fav_col
.
There’s no point making a distinction between objects and primitive types when you’re passing into a function. The important distinction to make is between mutable and immutable types. A function can mutate a mutable object passed in (making changes to an object the caller passed in) but it can’t mutate an immutable object. A function can reassign a mutable or an immutable object, but the caller won’t see these changes, because all reassignment does is move the invisible wire attached to the parameter.
Further reading
Ecma-262-5 in detail: Name binding Dmitry Soshnikov talks about the same issues here but in a lot more detail. He uses the more strictly correct term “rebinding” where I have said reassignment, as the variable name is “bound” to an object. Definitely worth reading if you want a more in-depth discussion.
ECMA-262-3 in detail: Evaluation strategy A discussion of all the different types of evaluation strategy, including call by value, call by reference, and call by sharing. JavaScript is noted to be call by sharing, or “call by value where value is the reference copy".
Call by sharing on Wikipedia Wikipedia notes that the Python community is the only one to have widespread use of this term, even though the same semantics are shared by Java, Ruby, Scheme, JavaScript etc.