When we assign a variable to another variable or we pass one or more variable(s) to a function as parameters, then we need to have a clear understanding of the pass-by-value and pass-by-reference.
Here is what we mean by pass-by value and reference-
DEFINITION
When we assign a variable to another one, or pass that variable to a function, then the following thing can happen.
- Pass by value: the value of the variable is copied to the new one and passes the value to the function.
- Pass by reference: the reference/address is passed to the new variable, and to the function.
In JavaScript, we can not explicitly control, if we want to pass/assign a variable by value or reference. JavaScript does that for us.
Here is the rule we should be aware of for pass by value and reference of a variable-
RULE OF THUMB
Here is the rule of thumb for variables passed by value vs reference-
- All primitive type variables(Number, String, Boolean, Symbol, etc.) are passed by value.
- All non-primitive type variables(object, array, etc.) are passed by reference.
Let’s check all the rules and cases of JavaScript variables to be passed by value or pass by reference-
Pass by Value
All primitive types of variables – Number, BigInt, String, Symbol, Null, etc. are always passed by value.
Case #1: Assign Variable to Another Variable
- Define a variable(bigBoxNum).
- Then assign that variable to another variable(anotherNum).
- Then at some point change the first variable(bigBoxNum).
- Check if changing the main(original) variable(bigBoxNum), also changes the value of the clone/secondary variable(anotherNum) value or not.
let bigBoxNum = 100;
let anotherNum = bigBoxNum;
// Change value of bigBoxNum
bigBoxNum = 21;
console.log("Value of bigBoxNum: " + bigBoxNum);
console.log("Value of anotherNum: " + anotherNum);
Output:
Value of the first(main) variable(bigBoxNum) is changed as expected.
However, the value of the second variable(anotherNum – to which the first variable was assigned) is not changed.
Value of bigBoxNum: 21
Value of anotherNum: 100
What is really going on here?
Primitive type variables are passed by value. So the value is copied when assigned to a new variable.
That is why when the original variable is changed, that does not change the clone variables. As cloned variable has a copy of the value.
Case #2: Pass Variable to Function by Value
- Define a function(changeMyNumber), that accepts one or more parameters(param1).
- In the function definition, write code for the implementation. Most importantly, change the value of the parameter(param1).
- Define a variable(bigBoxStr).
- Call the defined function(changeMyNumber) and pass the variable(bigBoxStr) to the function as parameter.
- After the function call, check if the value of the original variable(bigBoxStr) is changed or not.
var bigBoxStr = "Some custom string here, for demo";
function changeMyNumber(param1) {
// your other funcationality here
// Change the value of variable
param1 = 21;
}
// Call the function and pass bigBoxStr
changeMyNumber(bigBoxStr);
console.log("Value of bigBoxStr: " + bigBoxStr);
Output:
The value of the original variable(bigBoxStr) is not changed, it stores the original value.
Value of bigBoxStr: Some custom string here, for demo
What is going on here?
When a primitive type variable is passed to a function, JavaScript passes a copy of the value of the variable. So whatever function is performed inside the function, does not affect the original variable.
Pass by Reference
All the non-primitive variables – Object, Array, Function, etc. are always passed by reference. The memory address of the variable is passed, instead of the copied value.
So changing the non-primitive variable reference, will affect all the references.
Case #1: Assign Variable and Change Partially
- Define an object(bigBoxObj).
- Assign that object to another variable(anotherObj).
- Change some properties of the original object(bigBoxObj).
- Create an array(bigBoxArr), and assign some items.
- Define another variable(anotherArr) and assign the original array variable(bigBoxArr) to this new array.
- Change the original array(bigBoxArr), by adding some elements, or make other changes.
- Check if changing the original object and array, changes the value of the variables to which these variables are assigned.
// Define objects
let bigBoxObj = {
siteName: 'BigBoxCode',
siteURL: 'https://bigboxcode.com',
status: 'active',
whois: 'whois.custom.com',
};
let anotherObj = bigBoxObj;
// Change value of bigBoxObj
bigBoxObj.status = 'pending';
bigBoxObj.whois = 'changed.whois.com';
// Define arrays
let bigBoxArr = [1, 2, 3, 4, 5, 6];
let anotherArr = bigBoxArr;
// Change value of bigBoxArr
bigBoxArr.push(7, 8, 9, 10);
// Check the objects
console.log("Value of bigBoxObj: ", bigBoxObj);
console.log("Value of anotherObj: ", anotherObj);
// Check the arrays
console.log("Value of bigBoxArr: ", bigBoxArr);
console.log("Value of anotherArr: ", anotherArr);
Output:
Changing the original variables(bigBoxObj and bigBoxArr), also changes the cloned variables(anotherObj, anotherArr).
Value of bigBoxObj: {siteName: 'BigBoxCode', siteURL: 'https://bigboxcode.com', status: 'pending', whois: 'changed.whois.com'}
Value of anotherObj: {siteName: 'BigBoxCode', siteURL: 'https://bigboxcode.com', status: 'pending', whois: 'changed.whois.com'}
Value of bigBoxArr: (10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Value of anotherArr: (10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
What is going on here?
An object is passed/assigned by reference. So when we assign an object to another variable, both reference to the same object in memory. So changing one will affect another variable value.
Case #2: Pass Variable to Function and Change Partially
- Define a function(bigBoxFunc), and define a parameter(param1).
- Define an object(bigBoxObj).
- Call the function(bigBoxFunc) and pass the object(bigBoxObj) to the function.
- In the function change some properties of parameter(param1), which refers to the object(bigBoxObj).
- After the function call, check if the original object(bigBoxObj) is changed or not.
let bigBoxObj = {
siteName: 'BigBoxCode',
siteURL: 'https://bigboxcode.com',
status: 'active',
whois: 'whois.custom.com',
};
function bigBoxFunc(param1) {
// your other funcationality here
// Change the value of variable
param1.status = 'pending';
param1.whois = "some.premium.whois.com";
}
// Call the function and pass bigBoxObj
bigBoxFunc(bigBoxObj);
console.log("Value of bigBoxObj: ", bigBoxObj);
Output:
Properties of the original properties are changed.
Value of bigBoxObj: {siteName: 'BigBoxCode', siteURL: 'https://bigboxcode.com', status: 'pending', whois: 'some.premium.whois.com'}
What is going on here?
Object is passed by reference, so if object property is changed in a function, then it affects the original object.
Pass by Reference and Reassign
All the non-primitive variables(object, array, etc.) are passed by reference. If we change some property then the original variable(object, array) is changed also.
But, let’s check what happens, if we reassign a completely new value to the variable.
Case #1: Assign Variable and Change Completely
- Define an object(bigBoxObj) and add some property.
- Assign that to another variable(anotherObj).
- Assign a completely new object to the original variable(bigBoxObj).
- Check if this change affects other variables(antoherObj).
let bigBoxObj = {
siteName: 'BigBoxCode',
siteURL: 'https://bigboxcode.com',
status: 'active',
whois: 'whois.custom.com',
};
let anotherObj = bigBoxObj;
// Let's change bigBoxObj completely
bigBoxObj = {a: 1, b: 100, c: 1000};
console.log("After complete object change-");
console.log("Value of bigBoxObj: ", bigBoxObj);
console.log("Value of anotherNum: ", anotherObj);
Output:
Assigning a completely new object to the original variable(bigBoxObj) does not change the clone variables(anotherObj).
After complete object change-
Value of bigBoxObj: {a: 1, b: 100, c: 1000}
Value of anotherNum: {siteName: 'BigBoxCode', siteURL: 'https://bigboxcode.com', status: 'pending', whois: 'changed.whois.com'}
What is going on here?
Object type variables are assigned by reference. But, if we assign a completely new value to the original variable, then that will reference a completely new object in memory. And the second object variable will still point to the old object.
Case #2: Pass Variable to Function and Change Completely
- Define a function(bigBoxFunc), and accept a parameter(param1).
- Define an object variable(bigBoxObj), and add some properties.
- In the function definition, change the parameter(param1), and assign a completely new object to it.
- Call the function(bigBoxFunc) and pass the object variable(bigBoxObj).
- Check if the value of the variable(bigBoxObj) is changed or not.
let bigBoxObj = {
siteName: 'BigBoxCode',
siteURL: 'https://bigboxcode.com',
status: 'active',
whois: 'whois.custom.com',
};
function bigBoxFunc(param1) {
// your other funcationality here
// Change the value of variable completely
param1 = {a: 1, b: 100, c: 1000};
}
// Call the function and pass bigBoxObj
bigBoxFunc(bigBoxObj);
console.log("Value of bigBoxObj: ", bigBoxObj);
Output:
Value of the original variable(bigBoxObj) is not changed, after the function call.
{siteName: 'BigBoxCode', siteURL: 'https://bigboxcode.com', status: 'active', whois: 'whois.custom.com'}
What is really going on here?
If we assign a completely new object to an object variable passed to a function, then the parameter will point to a completely new object.
So the original object will not change and will have the old/original object vlaue.
Clone Non-Primitive Variables
Changing the original non-primitive variable(object, array, etc.) also changes other variables referring to the same value.
So, how to clone an array or object, so that changing the origin value does not change the clone variable?
Here we are discussing, how to clone an array or object safely. Let’s check in detail-
Clone Object
Here are the methods of cloning an object, so that values are safely cloned and changing the variable does not affect the clone object.
We have discussed 2 methods here-
Method #1: [Recommended] Spread Object Properties
This is the recommended method for cloning an object-
- Define an object variable(bigBoxObj). This is our original object.
- To clone this object, create another object with curly braces “{}”, and inside it use the spread operator(…) to spread the original object(bigBoxObj).
// Define objects
let bigBoxObj = {
siteName: 'BigBoxCode',
siteURL: 'https://bigboxcode.com',
status: 'active',
whois: 'whois.custom.com',
};
let spreadCloneObj = {...bigBoxObj};
// Change value of bigBoxObj
bigBoxObj.status = 'pending';
bigBoxObj.whois = 'changed.whois.com';
// Check the objects
console.log("Value of bigBoxObj: ", bigBoxObj);
console.log("Value of spreadCloneObj: ", spreadCloneObj);
Output:
Changing the original object, does not affect the cloned object.
Value of bigBoxObj: {siteName: 'BigBoxCode', siteURL: 'https://bigboxcode.com', status: 'pending', whois: 'changed.whois.com'}
Value of spreadCloneObj: {siteName: 'BigBoxCode', siteURL: 'https://bigboxcode.com', status: 'active', whois: 'whois.custom.com'}
Method #2: Use Object.assign()
- Use the Object.assign() method to clone an existing object.
- First parameter of the Object.assign() should be an empty object and the second parameter should be the Original object(bigBoxObj).
// Define objects
let bigBoxObj = {
siteName: 'BigBoxCode',
siteURL: 'https://bigboxcode.com',
status: 'active',
whois: 'whois.custom.com',
};
let assignCloneObj = Object.assign({}, bigBoxObj);
// Change value of bigBoxObj
bigBoxObj.status = 'pending';
bigBoxObj.whois = 'changed.whois.com';
// Check the objects
console.log("Value of bigBoxObj: ", bigBoxObj);
console.log("Value of assignCloneObj: ", assignCloneObj);
Output:
Changing the original object(bigBoxObj) properties does not affect the cloned object.
Value of bigBoxObj: {siteName: 'BigBoxCode', siteURL: 'https://bigboxcode.com', status: 'pending', whois: 'changed.whois.com'}
Value of assignCloneObj: {siteName: 'BigBoxCode', siteURL: 'https://bigboxcode.com', status: 'active', whois: 'whois.custom.com'}
Clone Array
Let’s see, how we can clone an array safely. We have a few methods here-
Method #1: [Recommended] Spread Array Elements
- Create a new array with square brackets “[]”.
- In the square brackets, use the spread operator(…) and spread the original variables(bigBoxArr).
// Define array
let bigBoxArr = [1, 2, 3, 4, 5, 6];
// Cloen array using spreading
let cloneArr = [ ...bigBoxArr ];
// Change value of bigBoxArr
bigBoxArr.push(7, 8, 9, 10);
// Check the arrays
console.log("Value of bigBoxArr: ", bigBoxArr);
console.log("Value of anotherArr: ", cloneArr);
Output:
This process will close the array and the newly cloned object will not reference to the original object.
Value of bigBoxArr: (10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Value of anotherArr: (6) [1, 2, 3, 4, 5, 6]
Method #2: Using Array.from()
- Use the Array.from() method to create an array from the original array variable(bigBoxArr).
- Just pass the array variable(bigBoxArr) to the Array.from() method.
// Define array
let bigBoxArr = [1, 2, 3, 4, 5, 6];
// Cloen array using Array.from()
let cloneArr = Array.from(bigBoxArr);
// Change value of bigBoxArr
bigBoxArr.push(7, 8, 9, 10);
// Check the arrays
console.log("Value of bigBoxArr: ", bigBoxArr);
console.log("Value of anotherArr: ", cloneArr);
Output:
This will clone the original array, to a new variable.
Value of bigBoxArr: (10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Value of anotherArr: (6) [1, 2, 3, 4, 5, 6]
Method #3: Using Array.prototype.concat()
- Use the Array prototype method concat() to clone an array.
- First take an empty array “[]”.
- Call the concat function on the empty array”[]” and pass the original array(bigBoxArr) to it.
// Define array
let bigBoxArr = [1, 2, 3, 4, 5, 6];
// Cloen array using Array.prototype.concat()
let cloneArr = [].concat(bigBoxArr);
// Change value of bigBoxArr
bigBoxArr.push(7, 8, 9, 10);
// Check the arrays
console.log("Value of bigBoxArr: ", bigBoxArr);
console.log("Value of anotherArr: ", cloneArr);
Output:
This array prototype function will take the original array and contact it to an empty array, which will result in an array that is equivalent to the original array.
And, this clone array is created newly, so it will not point/reference to the original one.
Value of bigBoxArr: (10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Value of anotherArr: (6) [1, 2, 3, 4, 5, 6]
Deep Clone
All the cloning methods that we discussed above, perform a shallow copy of the original variable(object, array) properties.
Let’s see what the is problem with shallow copy, and how can we perform deep cloning of variable properties.
Problem with Shallow Cloning
- Create an object that has nested properties. Here we have the object “bigBoxObj” and it has the property “info”, which has an object assigned to it.
- We have cloned the original object(bigBoxObj), using multiple methods.
- Let’s check the property inside “info” property.
- Check if the cloned objects are affected, or not.
let bigBoxObj = {
siteName: 'BigBoxCode',
siteURL: 'https://bigboxcode.com',
info: {
status: 'active',
whois: 'whois.custom.com',
}
};
let cloneObj1 = {...bigBoxObj};
let cloneObj2 = Object.assign({}, bigBoxObj);
// Change site name
// This will not affect other cloned objects
bigBoxObj.siteName = "BigBoxCode Updated";
// Change info.status in the main object
// This will affect clone objects
bigBoxObj.info.status = "This is Changed";
// Check values
console.log("Value of bigBoxObj: ", bigBoxObj);
console.log("Value of cloneObj1: ", cloneObj1);
console.log("Value of cloneObj2: ", cloneObj2);
Output:
“info” property of the cloned objects are affected, when we change the “info” property of the original object(bigBoxObj).
Value of bigBoxObj: {siteName: 'BigBoxCode Updated', siteURL: 'https://bigboxcode.com', info: {status: 'This is Changed', whois: 'whois.custom.com'}}
Value of cloneObj1: {siteName: 'BigBoxCode', siteURL: 'https://bigboxcode.com', info: {status: 'This is Changed', whois: 'whois.custom.com'}}
Value of cloneObj2: {siteName: 'BigBoxCode', siteURL: 'https://bigboxcode.com', info: {status: 'This is Changed', whois: 'whois.custom.com'}}
What is really going on here?
Here the object is cloned, but the “info” property has an object to it. And by rule, any object will be passed by reference. So only the info property object is referenced by both the variables.
This is the problem with shallow copy, as we don’t know how many nested properties are there, and how many levels of nesting is there.
Perform Deep Cloning
Here is how can we perform a deep copy of a nested object.
- First stringify the object with “JSON.stringify()”.
- Then retrieve the original object properties by parsing, using “JSON.parse()”.
let bigBoxObj = {
siteName: 'BigBoxCode',
siteURL: 'https://bigboxcode.com',
info: {
status: 'active',
whois: 'whois.custom.com',
}
};
let cloneObj = JSON.parse(JSON.stringify(bigBoxObj));
// Change site name
// This will not affect other cloned objects
bigBoxObj.siteName = "BigBoxCode Updated";
// Change info.status in the main object
// This will affect clone objects
bigBoxObj.info.status = "This is Changed";
// Check values
console.log("Value of bigBoxObj: ", bigBoxObj);
console.log("Value of cloneObj: ", cloneObj);
Output:
The cloned object is a completely new object, and it has all the properties of the original.
Value of bigBoxObj: {siteName: 'BigBoxCode Updated', siteURL: 'https://bigboxcode.com', info: {status: 'This is Changed', whois: 'whois.custom.com'}}
Value of cloneObj: {siteName: 'BigBoxCode', siteURL: 'https://bigboxcode.com', info: {status: "active", whois: "whois.custom.com"}}
Note
This operation of deep cloning might have performance issues, if the original object is very large and/or has so many nested properties.