Skip to content

Instantly share code, notes, and snippets.

@AloofBuddha
Last active June 7, 2023 16:17
Show Gist options
  • Save AloofBuddha/92ee6749a8be48c984881a4523f533c3 to your computer and use it in GitHub Desktop.
Save AloofBuddha/92ee6749a8be48c984881a4523f533c3 to your computer and use it in GitHub Desktop.
Pass by value vs reference in JS
// primitives are 'passed by value' in JS
// meaning the value of the variable is copied over into a new local variable in the function
let n = 1;
function addOne(x) {
x += 1;
return x;
}
let result = addOne(n);
console.log(n); // 1
console.log(result); // 2
/*
Breakdown:
n = 1 at the global level
addOne(n) is called, argument is evaluated to its actual value 1
internally in the addOne function, x is set to 1
we add 1 to the value of x itself and then return it, making the updated value available to the calling site
where we then store it in result
by printing both we can confirm the original input value of n is unchanged
*/
// let's now consider some caveats to the above idea
let n = 1;
function addOne(x) {
x += 1;
}
let result = addOne(n);
console.log(result); // undefined
console.log(n); // 1
/*
Breakdown:
Nothing changed. Why?
addOne(n) is called, argument is evaluated to its actual value 1
internally in the addOne function, x is set to 1
we add 1 to the value of x itself but never return it
x equals 2 before the function ends, but once the function ends this variable/value dissapears
if we don't return anything we return undefined by default and as expected the original value is unaffected
*/
// complex types are 'pass by reference' in JS
// meaning the *memory address* of the variable is copied over into a new local variable in the function
// meaning the internal function variable refers to the same exact thing (not a copy) as the original argument
let point = { x: 1, y: 2 };
function addOneToPoint(myPoint) {
myPoint.x += 1;
myPoint.y += 1;
}
addOneToPoint(point);
console.log(point); // { x: 2, y: 3 }
/*
Breakdown:
point is set to the object { x: 1, y: 2 } at the global level. Let's just say this lives at the memory address 42.
addOneToPoint(point) is called, argument is converted to it's memory address (42)
internally in the addOneToPoint function, myPoint is set to the object at memory address 42, so it 'references' the exact same object
as what was passed in as an argument
we access the x and y keys of myPoint and update them, then end the function (no return)
note that at the call site we don't set it to a new variable because it's unnecessary, the function itself mutated the passed in argument
by printing it we can confirm that passed in argument was mutated
*/
// some caveats for pass by reference
let point = { x: 1, y: 2 };
function alterPoint(myPoint) {
myPoint = { a: 1000, b: 2000 }
}
alterPoint(point);
console.log(point); // { x: 1, y: 2 }
/*
Note this didn't work, we didnt alter what point references. Why?
Breakdown:
point is set to the object { x: 1, y: 2 } at the global level. Let's just say this lives at the memory address 42.
alterPoint(point) is called, argument is converted to it's memory address (42)
internally in the alterPoint function, myPoint is set to the object at memory address 42, so it 'references' the exact same object
as what was passed in as an argument
myPoint is then set to a brand new object ( { a: 1000, b: 2000 } ) which lives at a totally different memory address (say 84)
we then end the function without returning anything. the variable myPoint is destroyed at the end of the function,
so what it was referencing (memory address 84) is also lost
by printing point we can confirm that passed in argument was unchanged
Just because we pass in the original by reference doesn't mean the interal variable can't be made to reference something new
and if that is done no changes will propagate to the original.
*/
// a caveat for pass-by-anything
let x = 1;
function addOne(x) {
x += 1;
return x;
}
let result = addOne(x);
console.log(x); // 1
console.log(result); // 2
/*
Just wanted to demonstrate 'varaible shadowing' here. the variable 'x' and the addOne parameter 'x' appear the same, but JS
is smart enough to keep them from conflicting
changing the internal addOne version of x has no effect on the global x
Shadowing does not effect anything with the same name outside of it's scope, but it will prevent you from referencing the global x
Just know this is normal practice, don't worry about using a function parameter name that is identical to the argument it is passing
in, there's no actual problem there.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment