The confusing part is when the variables hold an object, a function or an array. These are generally complex types, which means that the variable holds the reference/address of the object. Technically, again the pass by value concept is present. In the complex types, we pass by value the references!
When we access a primitive data type, we work on its value. Primitive types are Boolean, Number, String, Null, Undefined and symbol.
When we assign primitives to the variable, we copy the value. If any changes are made to the variable copied, the original variable will not be affected by them.
Imagine the memory as a storing apothecary where a series of boxes exist. These boxes are boxes of data and we use them to store our values. Upon declaring a variable, you were given a box for use and you will hold it until you need it later. So, in our example we allocate a box, we named it originalBox and put inside the string "doughnut". We can allocate another box, and name it to box and copy the content of the originalBox to it. What will happen if we want to change the box variable content? Simple we will remove the string "doughnut" from the box and put inside the string "apple"! Only the original value was copied when the box was created. The two boxes are not related to each other and not affect each other.
Passing a primitive variable as a function parameter means that we make a new local instance of the variable and copy the value. Any changes made to the parameter, leave the original variable unaffected.
In both of these examples, we pass the primitive value directly. Because we call the variable by value, any changes to the copied value do not affect the original variable.
There are also complex types as objects, arrays and functions. When you manipulate a complex type you work on a reference (memory address) to its value. In JS everything is passed by value, and here this argument stands. The memory address of the parameter at the time of the function call is passed to the function as the value to be associated with the corresponding copied parameter.
We will use the same example here, with the only difference of using a complex variable: an object!
Here in this example, when we create our variable JS recognizes that we creating a complex entity. That means in our variable we will store it's location in the storehouse. When we copy the variable, we copy the location that our original box is copied. That means that the 'boxObjCopied' is referencing the location of 'boxObject' and its content. What does that mean though? Because 'boxObjCopied' doesn't contain the value but a reference, when we update the property of it, we actually update the original object!
So, how we can make a shallow clone of the boxObject, and use it without altering its value?
We can use 2 ways: spread operator or Object.assign to deep clone the boxObject!
A more intermediate approach about the differences of spread operator and Object assign can be found here: Object.assign vs Object Spread in Node.js]
For deep clones see here: [Shallow clone vs Deep Clone]