In JavaScript and many other programming languages, variables are references to a value. By the Transitive Law of Logic and Mathematics, it then follows that references and values are one and the same. On the surface, this is true to some extent. Unfortunately, it isn't as simple as that under the hood.
Primitive and Non-primitive Data Types
Data types in JavaScript are classified as either primitive or non-primitive.
-
Primitive data types are the simplest of the two classifications. These include booleans, numbers, strings,
null
, andundefined
. If two primitives are compared using the strict equality operator (===
), the result istrue
if and only if the two primitives have the same type and the same value. - On the other hand, non-primitive data types have an added layer of complexity due to their data structure. These include objects, functions, and arrays. Comparing two non-primitive data types with the strict equality operator will only result to
true
if and only if the two data types exist as the exact same instance. In other words, they occupy the same place in memory.
NOTE: Symbols are also primitive data types, but the rules are sketchy for them. I will not expound on them in this article for the sake of simplicity.
In the Context of Primitive Data Types
// The variable dev is a reference to the value 'to'.
const dev = 'to';
// The variable going is a reference to the value 'to'.
const went = 'to';
The variables dev
and went
both have the same value. However, dev
and went
are not necessarily the same references. Although they both store the value 'to'
, they do not store the same instance of 'to'
. They occupy two different places in memory. dev
stores a different string in memory while went
stores another string in memory. The two variables just happen to store strings that have the same value. The diagram below illustrates this. The arrows represent what values each variable is referencing/pointing to.
Now, you may be wondering how to achieve two variables having the same reference and same value. To do that, we simply assign the reference to another variable.
// The variable dev is a reference to the value 'to'.
const dev = 'to';
// The variable went is a reference to the variable dev.
// Therefore, the variable went is also a reference to the value 'to'.
const went = dev;
In this case, the dev
and went
both have the same value and the same reference. The diagram below is updated to reflect what has changed under the hood by doing this.
The diagram shows that running the code now takes up less memory because both dev
and went
now point to the same string. There is no longer a need to store two separate instances of the same string in memory. In a larger scale, this can prove to be useful in memory optimization. Compared to the previous example, memory usage has essentially been cut to half.
In the Context of Objects and Arrays
The concept of pointers and references is further amplified by non-primitive data types. Consider the code below.
// The variable foo is a reference to an object.
let foo = {
name: 'milk'
price: 5000,
stock: 10
};
// The variable bar is a reference to an object.
let bar = {
name: 'milk',
price: 5000,
stock: 10
};
As we learned in the previous section, two variables may store the same value but not necessarily point to the same place in memory. We can prove this by altering their properties.
// Mutating the properties
foo.name = 'cereal';
bar.name = 'candy';
console.log(foo.name); // 'cereal'
console.log(bar.name); // 'candy'
// Proof that they are not the same object in memory
console.log(foo === bar); // false
Now, what happens if we make the two variables point to the same object?
// The variable foo is a reference to an object.
let foo = {
name: 'milk'
price: 5000,
stock: 10
};
// The variable bar is a reference to the variable foo.
// Therefore, the variable bar is also a reference to the same object.
let bar = foo;
Indeed, the two variables have the same references and the same values. As a consequence of this, mutating the properties of one variable also mutates the other.
// Mutating the properties
bar.name = 'bread';
bar.price = 2.5;
bar.stock = 15;
// Since foo and bar refer to the same object,
// changes in the properties of one reflect on the other.
console.log(foo.name); // 'bread'
console.log(foo.price); // 2.5
console.log(foo.stock); // 15
// Proof that they are the same object in memory
console.log(foo === bar); // true
This behavior also applies to arrays. Instead of properties, we are altering the individual elements of the array.
// Two variables with the same values
let someDogs = ['Lucky', 'Sparkles', 'Presto'];
let moreDogs = ['Lucky', 'Sparkles', 'Presto'];
// Mutating the corresponding elements
someDogs[0] = 'Fluffykins';
moreDogs[0] = 'Mittens';
console.log(someDogs[0]); // 'Fluffykins'
console.log(moreDogs[0]); // 'Mittens'
// Proof that they are not the same array in memory
console.log(someDogs === moreDogs); // false
We are now going to assign moreDogs
a reference to someDogs
.
// Two variables with the same reference and the same value
let someDogs = ['Lucky', 'Sparkles', 'Presto'];
let moreDogs = someDogs;
// Mutating moreDogs
moreDogs[0] = 'Fluffykins';
// Mutations in moreDogs reflect in someDogs
console.log(someDogs[0]); // 'Fluffykins'
console.log(someDogs); // ['Fluffykins', 'Sparkles', 'Presto']
// Proof that they are the same array in memory
console.log(someDogs === moreDogs); // true
Conclusion
Variables are simply references that point to a value. Two references that store the same value do not necessarily mean they point to the same place in memory.
In most cases, we do not have to be concerned about their differences. But in cases where it is absolutely necessary to care about performance and memory optimization (such as server maintenance), it pays to keep these differences in mind. For instance, one can write an implementation of a function by making sure that it returns references rather than brand new instances of the same value.
The "document.getElement
API" is a great example of such an implementation. Let's take the document.getElementById
method for example. Given a unique id
, the method returns a reference to the HTML element that has that unique id
.
// Example ID for HTML element
const id = 'someId';
// Passing in the same ID as an argument to both methods
const someElement1 = document.getElementById(id);
const someElement2 = document.getElementById(id);
// Testing if both variables point to the same place in memory
console.log(someElement1 === someElement2); // true
So if there comes a time where you need to point out their differences, use this valuable article as a basic reference.
Top comments (11)
Unless I'm missing something here this is absolutely untrue and I'm not sure what it is you were trying to say. Referencing isn't transitive in the first place and even if it were, if you want equivalence you also need reflexivity and symmetry.
I think the memory graph is confusing,
went
should point to'to'
, not todev
. Admittedly there's little difference withconst
values.Other than that good writeup, many beginners struggle with this when exposed to mutability.
Yes, I'm aware that
went
should point to'to'
. I only made it pointdev
for simplicity's sake, really. I hope it's still understandable nonetheless.And regarding the Transitive Law, I was just trying to say that believing "references are values because variables are references to a value" should be avoided. Sorry if that didn't come out right.
Anyway, thanks for pointing (See what I did there?) this out.
I would then argue in favor of rewording that sentence. The reason being that it might confuse people about what transitivity is (i.e. a property of a specific relation).
A reference to Frege's "Über Sinn und Bedeutung" might be a better fit.
nice one
Oh, wow. This is some deep philosophical stuff.
It is, though I must admit I personally detested the subject as I have very different views about what language is. But that's going way too far for a value/reference discussion :P
Anyway, all jokes aside, I appreciate how you brought up the limitations and flaws of my article. Hopefully future readers will note your points. Thanks!
Congratulations! This is a very useful reference for people starting to learn JavaScript.
Thank you so much! I really try my best to write informative articles.
This is a useful and excellent reference article you wrote.
As someone who is learning Javascript, this behavior seems like a truly frustrating bug waiting to happen. A referencing operator as a hint would have been nice. Thanks for explaining.
Don't worry about it, my friend. You wouldn't really encounter anything frustrating from it. At least from my experience with JavaScript, I haven't had any problems with it. I think it stems from the fact that I try my best to be aware of my memory usage. Nonetheless, there is nothing to worry about. I promise that. Unless you are really trying to break your program/script, you won't break anything at all.