Still confused about how passing variables in Javascript functions works? So was I, until recently. It took me some effort to understand, and I'd like to share my understanding with an example.
First try guessing the outcome of the following javascript snippet
Input
const number = 1983
const string = 'Adrian'
let obj1 = {
value: 'obj1'
}
let obj2 = {
value: 'obj2'
}
let obj3 = obj2;
let obj4 = ['a'];
function change(numberParam, stringParam, obj1Param, obj2Param, obj4Param) {
console.log('\nSTART logs in function');
numberParam = numberParam * 10;
stringParam = 'Ionut';
console.log('numberParam - ', numberParam);
console.log('stringParam - ', stringParam);
console.log('obj1Param.value in function before obj1Param = obj2Param assignment - ', obj1Param.value);
obj1Param = obj2Param;
console.log('obj1Param.value in function after obj1Param = obj2Param assignment - ', obj1Param.value);
console.log('obj2Param.value in function before obj2Param.value change - ', obj2Param.value);
obj2Param.value = 'changed'; // obj1Param.value = 'changed'; would yield the same result
console.log('obj1Param.value in function after obj2Param.value change - ', obj1Param.value);
console.log('obj2Param.value in function after obj2Param.value change - ', obj2Param.value);
//obj4Param = ['b'];
obj4Param.push('b');
console.log('obj4Parma - ', obj4Param);
console.log('END logs in function \n');
}
change(number, string, obj1, obj2, obj4);
console.log('number final - ', number);
console.log('string final - ', string);
console.log('obj1.value final - ', obj1.value);
console.log('obj2.value final - ', obj2.value);
console.log('obj3.value final - ', obj3.value);
console.log('obj4 final - ', obj4);
Before you read the output I suggest you read the explanation of Call by sharing on Wikipedia, which is the best explanation on the topic I found.
Output
START logs in function
numberParam - 19830
stringParam - Ionut
obj1Param.value in function before obj1Param = obj2Param assignment - obj1
obj1Param.value in function after obj1Param = obj2Param assignment - obj2
obj2Param.value in function before obj2Param.value change - obj2
obj1Param.value in function after obj2Param.value change - changed
obj2Param.value in function after obj2Param.value change - changed
obj4Parma - ["b"]
END logs in function
number final - 1983
string final - Adrian
obj1.value final - obj1
obj2.value final - changed
obj3.value final - changed
obj4 final - ["a"]
Ok, so what's going on?
-
number
andstring
primitives are "boxed"1 inNumber
andString
objects2 before passing. Boxed objects are always a copy of the value object, hence new objects (Number and String) are created in memory with the same primitive values. In the function execution (scope) they get "unboxed", their value is changed and placed in the new memory space, but once the function is over the new space in memory is cleared, with the original remaining unaffected. - a copy reference to
obj1
andobj2
is passed to the function, pointing to the same address of the "original" objects in memory (call by sharing)3. With theobj1Param = obj2Param
assignment in the function, bothobj1Param
andobj2Param
to the originalobj2
object in memory, so when changing its propertyobj2Param.value = 'changed'
it will also be visible outside of the function scope, after it is terminated.obj1Param.value = 'changed'
would have had the same effect after the assignment. - what about
obj4
?obj4param
is also a copy reference to theobj4
object (remember in Javascript arrays are objects), but with theobj4Param = ['b']
assignment it's pointing now to a newly created object (the['b']
array object), which is visible only in the function's scope and is destroyed when the function is over. Thus, it has no effect on the original object. On the other hand, a statement likeobj4param.push('b')
would have changed the original array and would display a["a", "b"]
value.
Shared with love from Codever. Use the Copy to mine functionality to copy this snippet to your own personal collection and easy manage your code snippets.
Top comments (7)
The Wikipedia reference is talking about Java - not JavaScript.
MDN: Primitive values:
The mental model you are presenting is way too complicated.
Have a look at this recent discussion.
Given that all primitive values are immutable it makes sense to unify the perspective of "how variables work".
At this point it should become clear that function arguments upon invocation simply work like this:
That's it!
Note - arguments vs. parameters - MDN: Parameter:
Bonus:
const
simply means that the binding is permanent. Primitive values are already immutable - (internally) objects (and arrays which are objects) however are not immutable.MDN: const:
The Wikipedia entry does mention Javascript too -
I have found this good article JavaScript, Ruby and C are not call by reference... will correct and update if this is the case...
The core message of that article is:
but the article also points out that the technical details around what a reference exactly is in any given language can be a bit prickly.
JavaScript isn't a low level language so the language spec simply describes how ECMAScript has to behave but does not specify how it has to be implemented. Engines such as V8 or SpiderMonkey can implement the runtime in whatever way they wish as long as the observable behaviour conforms to the specification.
So while it is correct to say that JavaScript behaves in the manner of "Call by Sharing", the implementation details are up to the actual runtime engine.
For a more terse reference - Call by Sharing:
The remainder I would rephrase this way:
And in the case of JavaScript primitive values are immutable so:
only applies to objects (aka compound data). For primitive values it is only possible to
What hampers most people is the metaphor they use to visualise "variables".
In Java courses they tend to get told that "variables" are like boxes:
That metaphor doesn't work very well for JavaScript. That is why I typically don't use the terms "variable" and "assignment" but rather "name" and "bind".
A more useful metaphor for
is writing "number" on a sticky note and sticking it the immutable number
1983
. Thencreates an entirely new immutable number
19830
and repositions the "number" sticky note to it.On the other hand
simply writes "numberParam" on another sticky note and sticks it on the same immutable number
1983
leaving two separate sticky notes on it. Nowcreates an entirely new immutable number
19830
and repositions the "numberParam" sticky note to it while leaving the "number" sticky note on the original1983
.So
is writing "number" on a permanent label and sticking it on that immutable number
1983
. That label cannot be "repositioned".And the beauty of the "sticky note" metaphor is that works for objects and arrays as well.
So while
obj4
is the permanent label for that particular array, the "sticky note"obj4[0]
(a separate (indexed) name) can still be "repositioned" (to a different value) and more "sticky notes"obj4.push('b')
can still be added.On a technical level these "sticky notes" (or "permanent labels") are just references - the JavaScript way.
^ this :)
Thanks for this explanation here and also the "variables just point to values" mindset: in this post.
(Sorry for the long comment below in advance)
So let me try to summarize what I have learned here (hopefully is correct):
a=1
, JS variable holds a reference (address) to the actual memory location that stores the value.b=[1,2,3]
, the variable holds a reference (address) to the locations storing other references to the actual values of 1, 2, 3.a=1;b=a;
b and a hold the same references/address to the same memory location. A new memory is allocated only if we try to assignb=2
for example. (what if we assignb=1
? since 1 is immutable, willa=1;b=1;
result in both a and b hold references tht point to the same place that storing that immutable 1?It is shocking that even for primitive types, the JS variable only holds a reference (address in JS) to the actual value.
So basically, 90%+ of the materials out there are wrong, like this
from developer.mozilla.org/en-US/docs/L...
I mainly have two questions here:
First: I learned most of the knowledge of a programming language internal from C++. Any reference materials that I can read about the virtual memory level of JavaScript? Like the stack vs heap in C++. For example in this blog, it describes
let name = 'John';
stores the value in the stack frame of the stack, which is wrong based on the discussion here (right?)Second: The second question I have is entirely the opposite. If I don't want to touch on any details about virtual memory, how can I learn or teach others about JavaScript? Since JavaScript is a high-level programming language, we should not care about memory (unless we need to optimize). But it seems like it is impossible to teach the correct concept without touching the memory and addresses.
I appreciate the new metaphor you suggested here for JavaScript, but now I worry if that variable metaphor idea will differ in different languages (Java vs JavaScript or even others). What is the point of learning that metaphor (I believe metaphor is an abstraction that should be helpful in quickly picking up different languages)? All in all, my second question is more about what is the right level of abstraction to learn/teach a programming language without touching the internals but learning/teaching it correctly?
That depends as the runtime can optimize. See Elements kinds in V8. Also there are Typed Arrays which hold the actual (uniformly typed) values.
Remember that the runtime is free to optimize whichever way it seems fit as long as the observable behaviour aligns with the language specification. However it is dictated that the conceptual behaviour of a value of a primitive type is that of an "immutable datum represented directly at the lowest level of the language". Immutability is easily explained with
String
s; you can't change a string, you can only create a new one, so on "assignment" thelet
name is essentially being rebound to the new string. ButBoolean
,Null
Undefined
,Number
,BigInt
, andSymbol
values all behave the same way by virtue of being primitive types. I seem to remember coming across some V8 source code where primitive JavaScript values (at least in some context) were represented by C++ classes.It's a simplified mental model which mostly works and is easy to grasp for beginners.
Again the runtime implementation isn't dictated. V8 has the "Ignition" interpreter and the "Turbofan" compiler so actual JS execution could be accomplished in very different ways depending on the level of optimization.
My metaphor isn't original. Given that in JavaScript primitive values are immutable it's unfortunate that the term "assignment" is being used as that implies PLOP (PLace-Oriented Programming), i.e. (re)placing a value inside a location giving rise to the notion of a "variable" as the value inside the location can vary. In value-oriented programming (typically practiced in functional languages)
is binding the name
number
to the value1983
whileis rebinding the name
number
to the result of thenumber * 10
expression. With that terminology it is the binding that changes, not the value.I only stumbled upon this because I got annoyed at
const
—until I discovered:"The
const
declaration creates a read-only reference to a value. It does not mean the value it holds is immutable—just that the variable identifier cannot be reassigned."So people keep saying that
const
works differently for primitive values and objects (overlooking completely that primitive values are already immutable). This is a perspective that becomes necessary because of the assignment/variable oversimplification that people learn in the beginning. The truth is much simpler:const
doesn't act on the value but on the binding—it's the binding that is constant and unchangeable. As a result theconst
behaviour is absolutely identical for primitive values and objects as the binding being constant does not affect the internal mutability of objects (which includes arrays).I hope I conveyed that going to that level isn't actually necessary. Once you eliminate the notion of variables and assignments and replace them entirely with names and bindings, everything behaves entirely consistently. It's just that beginners material insists on starting with variables and assignments and then never adjust that initial mental model.
Thanks for your reply, let me study more with the given directions.
My imdidate response to this
Yeah, it makes a lot of sense. For example, in C/C++, the "Optimization" I learned is like out-of-order execution (including speculation) and unfolding loops. I remember back then, I was shocked as well. Now I realize it is not just the JavaScript tutorials problem. One of the utimate problems is that the teaching materials don't separate what the language specifications are. But for non-specified behaviors, they are subjected to be changed/optimized. I think this is also a missing mental model puzzle here.
Back then, our university C++/Java course should also focus more on the language specifications first and make a clear separation between abstraction and implementation. Now I am confused about what I have learned (for all languages what their specifications/promises are).