DEV Community

Adrian Matei for CodepediaOrg

Posted on • Originally published at codepedia.org

Javascript call by value or by reference, actually by sharing

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);
Enter fullscreen mode Exit fullscreen mode

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"]
Enter fullscreen mode Exit fullscreen mode

Ok, so what's going on?

  • number and string primitives are "boxed"1 in Number and String 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 and obj2 is passed to the function, pointing to the same address of the "original" objects in memory (call by sharing)3. With the obj1Param = obj2Param assignment in the function, both obj1Param and obj2Param to the original obj2 object in memory, so when changing its property obj2Param.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 the obj4 object (remember in Javascript arrays are objects), but with the obj4Param = ['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 like obj4param.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.


  1. https://en.wikipedia.org/wiki/Object_type_(object-oriented_programming)#Boxing 

  2. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects 

  3. https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharinghttps://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing 

Discussion (4)

Collapse
peerreynders profile image
peerreynders • Edited on

number and string primitives are "boxed" in Number and String objects before passing.

The Wikipedia reference is talking about Java - not JavaScript.

MDN: Primitive values:

All types except objects define immutable 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".

// Bind immutable number `1983` to name `number`
let number = 1983;

// Bind immutable string `Adrian` to name `string`
let string = 'Adrian'

// Bind object instance `obj1` to name `obj1`
const obj1 = {
  value: 'obj1'
}

// Bind object instance `obj2` to name `obj2`
const obj2 = {
  value: 'obj2'
}

// Bind object instance `obj2` to name `obj3`;
const obj3 = obj2;

// Bind array instance `obj4` to name `obj4` 
const obj4 = ['a'];

// Bind an entirely new immutable number that 
// is the result of `1983 * 10` to name `number`
//
// The immutable number `1983` continues to exist 
// but without a name referring to it
// it will be eventually garbage collected
//
number = number * 10;

// Bind an entirely new immutable string 'Ionut' 
// to name `string`
//
// The immutable string `Adrian` continues to exist
// but without a name referring to it
// it will be eventually garbage collected
//
string = 'Ionut';

// Bind the immutable string 'changed' to 
// the `value` property on 
// the `obj2` object instance (via the `obj3` name)
// 
// The immutable string 'obj2' continues to exist 
// but without a name referring to it
// it will be eventually garbage collected
//
obj3.value = 'changed';

// Push immutable string 'b' onto array instance `obj4` (via `obj4` name)
obj4.push('b')
Enter fullscreen mode Exit fullscreen mode

At this point it should become clear that function arguments upon invocation simply work like this:

// Bind immutable number currently bound to `number` to name `numberParam`;
let numberParam = number;

// Bind immutable string currently bound to `string` to name `stringParam`;
let stringParam = string;

// Bind object instance `obj1` to name `obj1Param`;
let obj1Param = obj1;

// Bind object instance `obj2` to name `obj2Param`;
let obj2Param = obj2;

// Bind object instance `obj4` to name `obj4Param`;
let obj4Param = obj4;

// Bind an entirely new immutable number that 
// is the result of `1983 * 10` to name `numberParam`
//
// The immutable number `1983` continues to exist
// and the name `number` still refers to it.
//
numberParam = numberParam * 10;

// Bind an entirely new immutable string 'Ionut' 
// to name `stringParam`
//
// The immutable string `Adrian` continues to exist
// and the name `string` still refers to it.
//
stringParam = 'Ionut';

// Bind the immutable string 'changed' to 
// the `value` property on 
// the `obj2` object instance (via the `obj2Param` name)
// 
// The immutable string 'obj2' continues to exist 
// but without a name referring to it
// it will be eventually garbage collected
//
obj2Param.value = 'changed';

// Push immutable string 'b' onto array instance `obj4` (via `obj4Param` name)
obj4Param.push('b')
Enter fullscreen mode Exit fullscreen mode

That's it!

Note - arguments vs. parameters - MDN: Parameter:

  • Function parameters are the names listed in the function's definition.
  • Function arguments are the real values passed to the function.

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 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.

Collapse
ama profile image
Adrian Matei Author • Edited on

The Wikipedia entry does mention Javascript too -

Call by sharing (also known as "call by object" or "call by object-sharing") is an evaluation strategy first noted by Barbara Liskov in 1974 for the CLU language.[8] It is used by languages such as Python,[9] Java (for object references), Ruby, JavaScript, Scheme, OCaml, AppleScript, and many others.

I have found this good article JavaScript, Ruby and C are not call by reference... will correct and update if this is the case...

Collapse
peerreynders profile image
peerreynders • Edited on

The core message of that article is:

call-by-sharing is call by value, but the value is always a reference.

It's always pass by value, but the value of the variable is a reference.

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 caller and called routine communicate only through the argument and result objects; routines do not have access to any variables of the caller.

The remainder I would rephrase this way:

After the assignments of actual arguments to formal arguments, the caller and the called routine share values. If the called routine modifies a shared value, the modification is visible to the caller on return. The names used to denote the shared values are distinct in the caller and called routine; if a routine assigns a value to a formal argument variable, there is no effect on the caller. From the point of view of the invoked routine, the only difference between its formal argument variables and its other local variables is that the formals are initialized by its caller.

And in the case of JavaScript primitive values are immutable so:

If the called routine modifies a shared value, the modification is visible to the caller on return.

only applies to objects (aka compound data). For primitive values it is only possible to

assigns a value to a formal argument variable, there is no effect on the caller.


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:

  • A primitive data box can hold one primitive value. You can "vary" by you putting a different value in the box.
  • An object box holds a remote control (reference) to the object. If you want to change something about the object, do it through the remote control. There may be multiple remote controls with a connection to the same object.

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

let number = 1983;
Enter fullscreen mode Exit fullscreen mode

is writing "number" on a sticky note and sticking it the immutable number 1983. Then

number = number * 10;
Enter fullscreen mode Exit fullscreen mode

creates an entirely new immutable number 19830 and repositions the "number" sticky note to it.

On the other hand

let numberParam = number;
Enter fullscreen mode Exit fullscreen mode

simply writes "numberParam" on another sticky note and sticks it on the same immutable number 1983 leaving two separate sticky notes on it. Now

numberParam = numberParam * 10;
Enter fullscreen mode Exit fullscreen mode

creates an entirely new immutable number 19830 and repositions the "numberParam" sticky note to it while leaving the "number" sticky note on the original 1983.

So

const number = 1983;
Enter fullscreen mode Exit fullscreen mode

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.

const obj4 = ['a'];
Enter fullscreen mode Exit fullscreen mode

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.

Thread Thread
sleeplessbyte profile image
Derk-Jan Karrenbeld

^ this :)