Before we go into JavaScript of things, let's look at what Pass by Value and Pass by Reference actually means.
Quoting this answer on stackoverflow,
When a parameter is passed by reference, the caller and the callee use the same variable for the parameter. If the callee modifies the parameter variable, the effect is visible to the caller's variable.
When a parameter is passed by value, the caller and callee have two independent variables with the same value. If the callee modifies the parameter variable, the effect is not visible to the caller.
In essence, when you are passing a variable by reference, you are allowing a function to change the variable and hence bear the weight of side effects whatever that function did to your variable.
When passing by value, you give the function much lesser control. You will take into account only what is returned by the function. They can change the variables you pass in but that will not affect your variables.
But this concept is mostly outdated today. It is taught in colleges and for introductory classes but most modern languages choose to implement this way differently. Talking about modern languages, so does JavaScript.
JavaScript implements this concept with two types of data types: Primitives and Objects.
Instead of introducing two ways of passing variables to functions, we have two types of variables. The type of variable defines whether it is passed by value or by reference.
Primitives
There are 6 primitive data types in JavaScript:
- string
- number
- boolean
- null
- undefined
- symbol
These data types are represented at the lowest level and are immutable.
Immutability means that their properties cannot be altered at run time. Like,
let stringVar = 'this is one long string';
stringVar[4] = 'v'; // This is not possible
stringVar = 'this is another string'; // This is not barred by the datatype
But I thought JavaScript did not have types
Yes, JavaScript is a loosely typed language. This still means that JavaScript has data types. But these data types are not bound to any variable.
let variable = 'one';
variable = 1;
variable = true; // Totally fine executions
How does this relate to Value vs Reference?
Primitives are always passed by value in the truest form.
function crazy(data) {
data = '_*.!@!!@(U!())'; // let this function do what it ever it wants, it wouldn't hurt out primitive
}
const impData = 'this is data as a string';
crazy(impData); // rest assured, impData (primitive) is safe from manipulation by crazy()
Objects
Objects are the second kind of data type available in JavaScript.
If you are wondering where are Arrays, Maps and all those wonderful things, they are all just objects with special methods underneath.
Let's define an object named Person
:
const person = {
name: 'John',
};
This is how the structure would look in the memory.
As you can see, { name: 'John' }
is allocated a place in the memory and variable person is pointing to it.
Now, John has taken a role in life and is being reassigned.
const person = {
name: 'john',
};
const developer = person; // John has become a developer.
Let us look at the memory representaion for this change:
Now, we have a second variable developer
pointing at the same memory that person
did.
So, let's say developer
learns a new skill, he adds it to his skill array. And magically person
variable would have learned this skill too. Because both these variables share the same object in memory.
const person = {
name: 'john',
skills: ['hiking'],
};
const developer = person;
developer.skills.push('coding');
/* == comparison operator for objects just compares references */
console.log(person === developer); // true
What if a new person now joins the company and is also named 'John'?
const person = {
name: 'john',
skills: ['hiking'],
}
const person2 = {
name: 'john2',
skills: ['hiking'],
}
person.skills.push('coding');
/* Should person2 learn coding just because he has the same portfolio as the other John? */
console.log(person === person2) \/\/ false, even though they share the same reference, they point at different memory instances and are obviously two different objects.
So, it is not the properties that matter, it is the memory it points to.
Everything that is an object (objects, arrays, functions, maps) are passed by reference in JavaScript. Going back to our earlier crazy example,
function crazy(impData) {
impData.data = '_*.!@!!@(U!())'; // your important data just got crazified
}
const impData = {
data: 'suppper secret',
};
crazy(impData);
console.log(impData); // gone. changed to gibberish by crazy.
How would I protect my objects from crazy functions?
1. Write lesser crazy functions. More Pure functions.
Pure functions are those that do not produce side effects. They interact only with their arguments and do not change them in anyway.
These functions produce result only as their return value.
function sum(a, b) { \/\/ Pure function
return a+b;
}
function addValue(obj) { \/\/ Impure function
obj.value = 100;
}
But what if it is not your function? What if you are passing the object to a third party?
2. Spread it.
There is a ECMAScript Stage 4 proposal for using spread operator for objects available here. You can use it now with a Babel Plugin
function addValue(obj) { \/\/ Impure function
obj.value = 100;
}
const impObj = {
value: 10,
}
addValue({...impObj});
What you have essentially done here is to create a shallow copy of your impObj
. Now the addValue
can no longer hurt it by altering it's properties. You can think of it like deploying a replica.
There is also a less fancy way of doing this with Object.assign
But as you might have figured from the word shallow there are issues with cloning like this.
function doCrazy(obj) { \/\/ Impure function
obj.name = "Hehe"; \/\/No effect
obj.skills.push("another"); \/\/ That seems to be breaking the illusion
}
const person = {
name: 'John',
skills: ['hiking']
}
doCrazy({...person});
console.log(person);
By building a shallow clone we have only eliminated the possibility of crazy people meddling with the first level of your object. The levels below it are still references and can be manipulated/changed by other functions/entities.
3. Deepclone it.
The next solution is to take the object clone it and go deeper and deeper into the object, find them clone them too.
I don't know who you are, but if you are reference, I would clone you.
Luckily, there is a function to do that, cloneDeep.
Does this change the way I write code?
Well, it should. It should tell you why pure functions are so important in functional programming. It should tell you there are primitives and objects. and it should tell you how JavaScript implements Value vs Reference.
Top comments (1)
Por fin entiendo de qué va la diferencia entre una y otro