DEV Community

Discussion on: A "Gotcha" of JavaScript's Pass-by-Reference

Collapse
 
bytebodger profile image
Adam Nathaniel Davis • Edited

I'll admit that maybe I'm the one with the massive mental block on this one. Maybe I'm the one who's fighting against the simple solution of the Monty Hall Problem. But every single time someone tries to explain to me why JS has no pass-by-reference, they either ignore the simple, tactical, repeatable examples I've already provided, or they say/show something that actually only furthers my case.

In my first example, I show a basic process by which we 1) initialize two variables, 2) pass those variables into new variables, 3) mutate the new variables, and 4) output the values of the original variables. In the example, the first original variable (the primitive value) is unchanged - because it's passed by value. The second original variable is changed - because it's passed by reference. It's already shown above, but here's a stripped down version of it:

// initialize variables
let mostImportantNumber = 3.14;
let spanishNumbers = { one: 'uno', two: 'dos', three: 'tres' };
// pass variables 
let answerToEverything = mostImportantNumber;
let germanNumbers = spanishNumbers;
// mutate variables 
answerToEverything = 42;
germanNumbers.one = 'einz';
// output original variables
console.log(mostImportantNumber);  // 3.14
console.log(spanishNumbers); // { one: 'einz', two: 'dos', three: 'tres' }

It's obvious that something very different happens to the two original variables - mostImportantNumber and spanishNumbers.

We never performed any mutation directly on mostImportantNumber and, understandably, the value of mostImportantNumber remains constant. We never performed any mutation directly on spanishNumbers - yet the value of spanishNumbers is updated.

I don't know how I can make it any clearer than this. It's demonstrably, provably obvious that the newly-created variable germanNumbers maintains some kind of "link" back to its initializing variable spanishNumbers. It's demonstrable and provable because, when we update the members of germanNumbers, the change is reflected back on spanishNumbers.

As long as I've been programming, this "link" has been called a "reference". If you (or anyone else) wants to tell me that this "link" is not a "reference", then that's fine - but that leads me to your question:

Since passing a reference of the variable isn't even possible in JS, so why have a term for that anyway?

Because, in JS (and many other languages), the behavior of a passed primitive is demonstrably different than the behavior of a passed object.

It's really that simple. Why would we keep calling a "kick" a "throw" if it's demonstrably obvious that they're two different behaviors, and two different things are happening in those actions??? And why would we keep calling JS objects that have been passed "pass-by-value" when it's demonstrably obvious that they behave entirely differently from primitives that have been passed by value???

Collapse
 
unchar1 profile image
Askara Novaru • Edited

Because, in JS (and many other languages), the behavior of a passed primitive is demonstrably different than the behavior of a passed object.

Actually there's no practical difference between how JS passes a primitive value or an object. I'm sorry if I came off wrong. I was just pointing out where a lot of people who were arguing about it where coming from. As for the example you provided.

// initialize variables
let mostImportantNumber = 3.14;
let spanishNumbers = { one: 'uno', two: 'dos', three: 'tres' };
// pass variables 
let answerToEverything = mostImportantNumber;
let germanNumbers = spanishNumbers;
// mutate variables 
answerToEverything = 42; // This is an assignment, not a mutation
germanNumbers = { one: 'einz' } // This would be the equivalent operation for an object
// output original variables
console.log(mostImportantNumber);  // 3.14
console.log(spanishNumbers); // { one: 'uno', two: 'dos', three: 'tres' } <-- Unchanged
Enter fullscreen mode Exit fullscreen mode

The JS runtime has no reason to treat a primitive assignment differently to an object assignment. In fact, you can even try to mutate a primitive just like an object, and JS will allow you to do that as well. It just throws away any mutations you make, which is why it appears that you are operating on another copy of the primitive. But in reality both variables point to the same primitive as well.

answerToEverything.one = 42; // Perfectly valid, it just doesn't mutate 'answerToEverything', since primitives are immutable by default
germanNumbers.one = 'einz'; // Since objects are mutable by default, this mutates 'germanNumbers'
Enter fullscreen mode Exit fullscreen mode

In fact, if you freeze the object, you can make an object immutable as well, and essentially get the same behavior between objects and primitives, which demonstrates that you don't need to copy anything to make an object behave like a primitive.

// initialize variables
let mostImportantNumber = 3.14;
let spanishNumbers = { one: 'uno', two: 'dos', three: 'tres' };
spanishNumbers = Object.freeze(spanishNumbers); // We freeze the object i.e. make it immutable like a primitive
// pass variables 
let answerToEverything = mostImportantNumber;
let germanNumbers = spanishNumbers;
// mutate variables 
answerToEverything = 42;
germanNumbers.one = 'einz'; // This mutation doesn't do anything, since the object is 'frozen' i.e. immutable
// output original variables
console.log(mostImportantNumber);  // 3.14
console.log(spanishNumbers); // { one: 'uno', two: 'dos', three: 'tres' } <-- Unchanged
Enter fullscreen mode Exit fullscreen mode

I suppose you could say that primitives are like objects that are just frozen by default. While this is technically not true, unless you attempt any operation specific to a primitive (such as addition,subtraction,etc), for the JS Runtime, they are treated exactly in the same way (such as when assigning them to variables or passing them to functions)

I hope that helps clear things up a bit!

Some comments have been hidden by the post's author - find out more