DEV Community

Cover image for Troll Hunting 101: JavaScript Passes Objects by Reference
Adam Nathaniel Davis
Adam Nathaniel Davis

Posted on

Troll Hunting 101: JavaScript Passes Objects by Reference

Sometimes I fall into the trap of thinking that I've seen it all. I may not know every technology. But I'm usually familiar with all the major movements-and-mindsets that pulsate throughout the programming communities.

So I was quite shocked recently when Dev.to introduced me to an entirely different species of troll. One I'd never encountered in my quarter century of programming experience.

I'm talking about the pass-by-reference troll.


Alt Text

How I Discovered the Pass-by-Reference Troll Species

I was writing an article, right here on this site, about cloning objects in JavaScript. (You can read it here: https://dev.to/bytebodger/cloning-objects-arrays-in-react-5475) I gave the following example:

const phantomMenace = { master: 'palpatine', apprentice: 'maul' };
const attackOfTheClones = phantomMenace;
attackOfTheClones.apprentice = 'dooku';
console.log(phantomMenace.apprentice);  // dooku(!)
Enter fullscreen mode Exit fullscreen mode

Before the code example, I made this statement:

In most programming languages, objects (and their nephews, arrays) are passed by reference.


To be honest, I never really gave any more thought to it as I wrote the rest of the article. To me, saying that "in most programming languages, objects are passed by reference" is akin to saying that "in most lakes, the water is wet." This wasn't some radical new idea I was putting into the blogosphere to challenge traditional mores. It was a basic statement of Dev 101.

Then a particular commenter latched onto my post, starting with his contention that almost no languages feature pass-by-reference. And that JavaScript has no pass-by-reference. And that I apparently have no idea how JavaScript - or any other language works. And the commenter was adamant.

To be clear, the commenter wasn't particularly accurate. Nor persuasive. First, they started sending me examples in Java - as though Java and JavaScript are interchangeable. Then they started telling me that code I put in my examples didn't do what I said it would do (it absolutely did - and I had the StackBlitz's to prove it). Then they just kept diving into circular arguments - even going so far as to put up their own code examples that only served to illustrate my point.

Nevertheless, this particular commenter was so vehement that it actually led me to question my premises. Had I been misunderstanding a critical detail for most of my professional life? After all, I want to be open-minded. And anything's possible...

So I reached out to a friend of mine. He's about 20 years younger than me (so he has none of that old-guy stink in his thought patterns). And he's one of these guys who thinks in code. He's highly entertained by esoteric questions of coding theory. He's basically a prodigy. And quite frankly, he's a far better programmer than I. So I shot him this basic question:

ME: Hey, sorry for the silly, basic, theoretical question, but I gotta ask this as part of a little personal experiment: In JavaScript, objects are passed by... value? By reference? By... something else?? It's not a trick question. What is your knee-jerk answer??


Within a minute or two, he replied:

PRODIGY FRIEND: By reference


A few minutes later, he elaborated with this:

PRODIGY FRIEND: Pass an object into a function and then change someObject.someProperty and the original object in the original scope will also change. Was someone arguing this point with you? LOL




Alt Text

Troll Sightings in the Wild

Initially, I brushed off this "rogue commenter". I'd never heard anyone else go so bat-shit crazy about such a basic concept in programming. And then... I started seeing more of them. Here. On Dev.to. (And, curiously enough, only on Dev.to - don't know exactly what that means.)

And they're jerks. I'm sorry to put it in such confrontational terms. But it's true. They feel compelled to jump on any post they can where someone dares to claim that JavaScript objects are passed by reference. And many of their responses are pedantic bullying, where they expect you to prove that they're wrong.

At first, I had a really hard time even describing some of the ridiculous comments I saw on this site with regard to pass-by-reference. But then it hit me.

You know what the JavaScript Pass-By-Reference Trolls are just like???


Alt Text

Knowledge is the Enemy

Do pass-by-reference trolls and flat-earthers have their own dating app? Do they send each other secret admirer letters? Are they the same people???

If you've ever tried to argue with a flat-earther, you know that it's an exercise in futility. They won't listen to anything you say. They will unrepentantly use all manner of logical fallacies. They don't mind mashing up examples from any scientific discipline to "further" their objectives.

One day, you go to sleep content in the knowledge that some ideas have been so universally proven that they cannot be refuted as scientific fact. The next day, you have some belligerent flat-earther moron sitting at your table telling you that you must prove to him every aspect of your ridiculous and specious understanding (the same "understanding" that's upheld by every scientist and educated individual on the face of the planet).

It's quite the same with JavaScript's pass-by-reference trolls. Once they hear you commit the Cardinal Sin of discussing JavaScript's pass-by-reference, they will pounce.


Alt Text

An Epic Waste of Time

When I was first trying to treat these people seriously, I tried to make the case as dead-simple as possible:

// Are you telling me that THIS:
const phantomMenace = 'first movie';
let attackOfTheClones = phantomMenace;
attackOfTheClones = 'second movie';
console.log(phantomMenace); // first movie

// Is functionally no different than THIS???
const phantomMenace = { master: 'palpatine', apprentice: 'maul' };
const attackOfTheClones = phantomMenace;
attackOfTheClones.apprentice = 'dooku';
console.log(phantomMenace.apprentice);  // dooku(!)
Enter fullscreen mode Exit fullscreen mode

In both examples, we have an original variable. A second variable was created, initialized by the first variable. The second variable was then mutated. And... here's where the examples are unique: In the first example, the original variable is unchanged. In the second example, the original variable absolutely is changed.

The difference is that, in the first example, the values are primitives. Primitives are always passed by value. Another way to think of "passed by value" is that the value is passed as a fresh copy of the original.

But in the second example, the values are objects. Objects are always passed by reference. Another way to think of "passed by reference" is that the value passed is a pointer to the original value. You know what's another name for "pointer"??? Reference

I don't know how I could make it any clearer than that. And yet, I gave this exact example to a pass-by-reference troll and asked him, "Are you telling me that the values in both of these examples are passed by value??" And he said, "Yes."


Alt Text

Rules Lawyers

Although it's honestly difficult to follow most of their dogmatic rantings, one thing is clear: Many of them revel in being rules lawyers. They don't seem to care so much about how code works (like: in the example shown above). They care about pedantic details about how code is defined. In stodgy spec documents.

One example of this is when they say things like:

Please show me in the ECMAScript spec where it says...


Nope. Uh-uh. Not even going there. My working code sample is above. I don't care about the exact terminology that someone chose to use when writing a spec doc as part of a bureaucratic open-source committee.

I don't doubt that the words "pass by reference" might not actually exist in the ECMAScript spec. That in no way overrides the empirical fact that I can illustrate objects being passed by reference in the extremely simple example above.

I don't care so much about the exact verbiage chosen when people decided to write out a lengthy spec. I care about how the code actually works.

I don't know if the Java spec actually uses the words "object-oriented language". Maybe it does. Maybe it doesn't. I honestly couldn't care less. Even if those words exist nowhere in the Java spec, the simple fact is that Java is an object-oriented language.

Similarly, I really don't care if the words "pass by reference" exist in the JavaScript spec. JavaScript does pass objects by reference.


Alt Text

What is the Meaning of "is"??

The pedantics don't stop with snobbish references to the ECMAScript spec. Some of the trolls wanna challenge the meaning of the word "value". They typically say something like this:

When you set one variable equal to an object, it's still set by value. It's just that the value is a reference to another object.


Yes. I've heard/read that exact effery. This is not some sad joke. It's a banal reality.

Under this mindset, there is no such thing, in the entire programming world - or even in any other part of the physical world - as a "reference". Because, under this thinking, every "reference" is, in itself, a type of "value" (that holds... a reference).

Now I'm sure that Plato and Socrates would love to toss that distinction around for a few decades while they work out the difference between "a reference" versus "a value - that holds a reference".

But I don't have the time to wait for dead philosophers to figure out whether trees, falling in the woods, actually make a sound if no one is there to hear it. I have to write code. And meet deadlines. And explain esoteric coding concepts to junior developers.

When I'm trying to show a new developer the difference between the two examples shown above, you can sure-as-hell bet that I don't tell them, "You see... It turns out that all of these variables are passed by value! And you'll just have to pray to some ancient god to determine why the behavior is so different."


Alt Text

The Pedantry of "Passing"

The pedantry doesn't stop at the definition of "value". The trolls also wanna argue about "pass". Under this strain of trollishness, the argument goes:

In your examples shown above, there is no "passing" taking place - because there are no functions.


OMFG.

Despite what you may surmise from these blogs, I actually speaka de English reals good. And I know what "pass" means.

Yes, I thoroughly understand that, in programming, "passing" often refers to the idea that we pass arguments into functions. But when you do this:

const firstVariable = 'foo';
const secondVariable = firstVariable;
Enter fullscreen mode Exit fullscreen mode

There is nothing logically incorrect in saying that we have passed the value of firstVariable into the initial state of secondVariable. If that's not your preferred verbiage, then fine - whatever. But that doesn't mean it's wrong to say that the value of firstVariable was passed into secondVariable.

But let's set aside the Merriam-Webster definitions of "passing" for a moment. Because even if we accept that pass-by-reference and pass-by-value can only apply when we are passing values into a function, JavaScript STILL passes by reference!

Wanna argue with me, trolls?? Here's the oh-so-simple example:

const myObject = {
  one: 'uno',
  two: 'dos',
};
const myString = 'marco';

const myFunction = (theObject, theString) => {
  theObject.one = 'einz';
  theString = 'polo';
}

myFunction(myObject, myString);
console.log('myObject', myObject); // {one: 'einz', two: 'dos'}
console.log('myString', myString); // 'marco'
Enter fullscreen mode Exit fullscreen mode

Inside myFunction(), when theObject is mutated, the change is reflected back in myObject. Wanna know why??

Because JavaScript passes objects by reference.

Also inside myFunction(), we mutated theString. But that change was not reflected back in myString. Wanna know why??

Because JavaScript passes primitives by value.


Alt Text

More Than Terminology

It's tempting to paint this as just a simple mismatch in terms. Nothing more than a misunderstanding, if you will.

We could say that JavaScript objects are "copied as pointers". And that JavaScript primitives are "copied as literals". For some minds, that might be a clearer definition. Depending upon your chosen dogma, it might sit easier on your mind.

But... no. That's BS. Sorry (not sorry), but it just is.

You see, if you wanna argue that a "reference" is just "a value that holds a reference" - then you're gonna argue the same thing about a "pointer". To be clearer, if you wanna make this kinda argument, then... you just enjoy arguing.

And if you have a problem with "pass" because you think it can only possibly be applied to functions - well then... you just don't have a solid grasp of the English language. I'm sure that sounds snarky and combative - but it's absolutely true.


Alt Text

The Power of Words

This rant may lead you to believe that I don't care at all about technical definitions or that I play fast-and-loose with jargon. But nothing could be farther from the truth.

In all areas of life, but especially in tech, I fully understand that words can have extremely specific meanings. And that botching those meanings can have real world consequences.

But I also understand that words portray concepts. If we focus on the words for the sake of arguing about specific words, then we've missed the entire point. Language only has meaning in context.

So when you wanna get on your rules-lawyering high horse about passing by reference, or passing by value, you need to keep in mind one extremely basic - and tactical - fact. It would seem, on the surface, that these two examples should behave in the exact same way:

// Are you gonna telling me that THIS:
const phantomMenace = 'first movie';
let attackOfTheClones = phantomMenace;
attackOfTheClones = 'second movie';
console.log(phantomMenace); // first movie

// Is functionally no different than THIS???
const phantomMenace = { master: 'palpatine', apprentice: 'maul' };
const attackOfTheClones = phantomMenace;
attackOfTheClones.apprentice = 'dooku';
console.log(phantomMenace.apprentice);  // dooku(!)
Enter fullscreen mode Exit fullscreen mode

But of course... they do not behave in the same way. In the first example, the changes to the mutated object are reflected back on the source object. In the second example, the changes to the mutated string are not reflected back on the source string. Why???

Because primitives in JavaScript are passed by value. And objects in JavaScript are passed by reference.


If you think that the examples above are operating under the exact same principle - that all the variables are passed by value - well... good luck to you. Enjoy yelling at people over pedantic details for the rest of your life - details that only you, with your heroic knowledge, can possibly understand.

[FINAL NOTE: This will be the 53rd article I've published on Dev.to. I've always taken pride in answering most of the comments that are left on my blogs. For this particular article, I won't be responding - to anything. I've learned from reading around on this subject that the "there is no pass by reference!" crowd can be, quite honestly, jerk-wads. (Technical term.) And I have no desire to engage with the flat-earthers for another round of pigeon chess.]

Top comments (7)

Collapse
 
thomaslevesque profile image
Thomas Levesque

This is interesting.

I don't know a lot about Javascript, I'm primarily a C# developer. In C#, there are basically two kinds of types:

  • value types: primitive types (numeric types, boolean...) and structures. They're called "value types" because the variable directly contains the value. So when you copy one variable into another, the value itself is copied (i.e. "pass by value", although in C# this phrase typically means something else, which I'll come to later)
  • reference types: all other types, e.g. classes, strings, etc. They're called "reference types" because the variable doesn't directly contain the value, it contains a reference. When you copy one variable into another, the reference is copied, it still refers to the same object (i.e. "pass by reference", with the same caveat as earlier).

The thing is, in C# there's also another concept: passing arguments by reference or by value. And the default is to pass arguments by value. If I call a method that accepts an object (which is a reference type), the argument is passed by value... But the argument just contains a reference, so effectively the object is passed by reference. Passing the argument by reference (which is always explicit, using the ref keyword) means that the callee can reassign the argument (i.e. assign a different reference to it), and the caller will see the updated argument.

This confuses beginners to no end, so I find it interesting that this debate also exists in the JavaScript world!

BTW, I think this argument (which you present as being used by trolls) is valid and doesn't change the fact that in practice, objects are passed by reference:

It's just that the value is a reference to another object.

I think seeing it that way actually helps understand how it works: the variable's value isn't the object itself, it's a reference to the object. When you copy that variable to another variable, the reference is passed by value, but it's still a reference to the same object, so for all practical purposes, the object is passed by reference. (Yes, I realize this could be seen as pedantry, but I think it gives an interesting perspective on how things actually work under the hood).

Collapse
 
bytebodger profile image
Adam Nathaniel Davis • Edited

This confuses beginners to no end, so I find it interesting that this debate also exists in the JavaScript world!

I didn't even realize that there was much of a "debate" about this - until I started reading Dev.to articles and noticed the disturbing number of people who feel compelled to jump in every single time they see any writer commit the sin of implying that JS has pass-by-reference.

BTW, I think this argument (which you present as being used by trolls) is valid and doesn't change the fact that in practice, objects are passed by reference:

Agreed. And to be clear, I'm not saying that everyone who discusses the inner workings of JS pass-by-value/reference is a troll. But I've seen numerous cases, where someone references JS pass-by-reference, and one-or-two users feel compelled to litter the comments with a bunch of base refusals that "pass-by-reference" even exists in JS.

I think seeing it that way actually helps understand how it works: the variable's value isn't the object itself, it's a reference to the object.

Totally agree.

(Yes, I realize this could be seen as pedantry, but I think it gives an interesting perspective on how things actually work under the hood).

I don't think you're being pedantic at all. And there's nothing wrong with diving deeply into a language's gears to understand how it's really working. Here's where I've seen this turn into pedantry:

A dev copies an object into a new variable, alters some of the keys/values inside the new variable, and is then surprised to find the original object has been altered. When someone explains that this is because objects are passed by reference, some Snark Lord jumps in to "correct" them because, "JS always passes by value".

In most scenarios, if I say that this variable holds a reference, and someone tries to correct me by saying that the variable holds "a value - which holds a reference" - that someone is probably being a jerk. Even worse, they're probably making the subject much more confusing to anyone else who doesn't have a firm grasp of the underlying mechanics.

Imagine if you tell me that your couch is in your living room. And I decide that I should "correct" you by stating that there's an area in your living room, which contains the couch.

It's basically like having that guy in the room who feels compelled to constantly correct minor details in everyone else's conversation by butting in with, "Actually..."

Collapse
 
iquardt profile image
Iven Marquardt

I think (but am not sure) that Javascript actually pursues a call-by-sharing strategy for reference types (aka object types). With call-by-reference the callee should be able to reassign the caller's variable, right? Here is an example:

foo = xs => {
  xs = [1];
  return xs;
};

let xs = [];

foo(xs);
xs; // []

With call-by-ref xs should be [1], because the actual reference of the variable is passed. So what passes call-by-sharing then? Frankly, I don't know. I read somewhere it would only pass a copy of the reference. This doesn't make much sense to me though. A copy of a pointer to memory?

Anyway, this is just nitpicking. Essentially Javascript uses call-by-ref.

Collapse
 
devdufutur profile image
Rudy Nappée • Edited

Hi @bytebodger , I always like reading your posts, and the iconoclastic way you're doing it ;)

But here, I have to disagree (sorry !) when you say :
"You know what's another name for "pointer"??? Reference"

Pointers and references doesn't work the same way. When you reassign a pointer locally, the caller keep the former pointer. With a reference, if you reassign the variable, the caller gets the new value.

Take the following in C++ (a language which allow explicit pointers & allow you passing parameters by reference or by value) :

// Example program
#include <iostream>
#include <string>

class SomeStuff {
    public:
        int someInt;
        SomeStuff(int anInt) {
            someInt = anInt;
        }
};

void reassignReference(SomeStuff & someStuff) {
    someStuff = SomeStuff(42); // the referenced value has been changed
}

void reassignPointer(SomeStuff * someStuff) {
    someStuff = new SomeStuff(42); // the pointer is reassigned locally
}


int main()
{
  std::cout << "This is call by reference !" << std::endl;
  SomeStuff three(3);
  std::cout << "before reassignReference() : " << three.someInt << std::endl;
  reassignReference(three);
  std::cout << "after reassignReference() : " << three.someInt << std::endl;

  std::cout << "This is call by value !" << std::endl;
  SomeStuff * four = new SomeStuff(4);
  std::cout << "before reassignPointer() : " << four->someInt << std::endl;
  reassignPointer(four);
  std::cout << "before reassignPointer() : " << four->someInt << std::endl; // the pointed object hasn't changed
}
Enter fullscreen mode Exit fullscreen mode
This is call by reference !
before reassignReference() : 3
after reassignReference() : 42
This is call by value !
before reassignPointer() : 4
before reassignPointer() : 4
Enter fullscreen mode Exit fullscreen mode

Do we agree that if JS behave the same way than first portion, it means JS uses call-by-reference, if not it uses call-by-value ?

Let's try :

function reassign(someStuff) {
    someStuff = { someInt: 42 };
}

console.log("This is call by -what- ?");
let three = { someInt: 3 };
console.log("before reassign", three);
reassign(three);
console.log("after reassign", three);
Enter fullscreen mode Exit fullscreen mode

output :

This is call by -what- ?
before reassign {someInt: 3}
after reassign {someInt: 3}
Enter fullscreen mode Exit fullscreen mode

It seems parameters are passed by value in JS.

Are we agree now ? :)

Collapse
 
gadreash profile image
gadreash • Edited

@bytebodger , excellent article! I have coded in c++ before and am now on the Java/ JavaScript spectrum. Instead of debating pass-by-value vs by-ref semantics, people should focus on understanding what is the end result.
Agreed the aforementioned terms might mean the same but the situation/usecases in which they become relevant is where your expertise/mastery of the language comes into picture.
I usually visualize this problem using food (food always makes things easier ;)).
Function argument vars are like bowls. Apple in that bowl is like an object passed to the function. If the function takes that apple and inserts something inside it then the function caller will see that change in the apple.
On the other hand if the function replaces that apple with an orange then the apple remains unchanged in the scope where the function was called from.

If you are able to understand the above then you are set for life. If people really want to debate what should something be called then they can focus on Potato/Tomato pronunciations!!
PS: I used function for example, the above analogy works even for regular var to var assignment/copy :)

Collapse
 
functional_js profile image
Functional Javascript • Edited

Ha ha. I know the troll you're talking about.
They have nothing better to do than waste everybody's time with hyper-intensive bikeshedding.

Just like you said, in Javascript, like C# and Java, you can create new variables that point to reference types (heap objects). (regardless of whether you pass them into a func or not).

Passing a reference type into a func simply allocates a new stack frame to the heap object (the referenced object on the heap). We programmers call this pass by reference, because the heap object itself is not copied. We've passed a new reference to the heap object. If the trolls want to call it something else then go ahead. You do you.

Other languages like C# have a ref keyword for "passing reference types by reference". This also allocates a new variable on the stack just like "pass by reference", but points to the other stack variable, which points to the heap object.
Of course nobody is saying this "allocate stack reference to a stack reference" exists in JavaScript, but the trolls want to troll and try to trick their victims into saying they meant that.

Keep up the good work Adam.

Collapse
 
macsikora profile image
Pragmatic Maciej

I agree. Passing by reference is synonymous of passing by value being the reference. Nothing wrong with that choice of words. And as you have spotted it's clear for newcomers.