In section 4.5 Method Parameters of his book Core Java, Cay S. Horstmann explains that Java always uses the call by value behaviour for method parameters, never call by reference. I have discovered that the same appears to be true of JavaScript (and Python, and many more languages, I am sure).

Call by value and call by reference

Regarding the call by value and call by reference behaviours, Horstmann writes:

“Let us review the computer science terms that describe how parameters can be passed to a method (or a function) in a programming language. The term call by value means that the method gets just the value that the caller provides. In contrast, call by reference means that the method gets the location of the variable that the caller provides. Thus, a method can modify the value stored in a variable passed by reference but not in one passed by value.”

Primitives are passed by value

The call by value behaviour is most obvious with primitive values, which are immutable. This means they cannot be mutated in the same way that objects can. A variable that is assigned to a primitive value can be reassigned to another primitive value, but the primitive value itself cannot be mutated. There are seven primitive data types in JavaScript:

  • number
  • bigint
  • boolean
  • string
  • symbol
  • null
  • undefined

For example, let us define a toggleBoolean(x) function that attempts to change the value of a variable from true to false or from false to true.

function toggleBoolean(x) {
x = !x;
console.log(`End of function: x = ${x}`);
}

let trueOrFalse = true;
console.log(`Before: trueOrFalse = ${trueOrFalse}`);
toggleBoolean(trueOrFalse);
console.log(`After: trueOrFalse = ${trueOrFalse}`);

The output of this code is as follows. Note that the value of the trueOrFalse variable is unchanged after the function call. This proves that primitive values are passed by value in JavaScript. If they were passed by reference, the value of the trueOrFalse variable would change.

Before: trueOrFalse = true
End of function: x = false
After: trueOrFalse = true

The only way to “make this work” is to return the new value of the x parameter from the toggleBoolean(x) function and reassign the trueOrFalse variable to it.

function toggleBoolean(x) {
x = !x;
console.log(`End of function: x = ${x}`);
return x;
}

let trueOrFalse = true;
console.log(`Before: trueOrFalse = ${trueOrFalse}`);
trueOrFalse = toggleBoolean(trueOrFalse);
console.log(`After: trueOrFalse = ${trueOrFalse}`);

The output of this code is as follows:

Before: trueOrFalse = true
End of function: x = false
After: trueOrFalse = false

Objects seem like they are passed by reference

Following on from this, Horstmann writes:

“You have seen that it is impossible to change a primitive type parameter. The situation is different for object parameters.”

For example, let us define an increment(x) function that increments the value of an object’s .value property:

function increment(x) {
x.value += 1;
console.log(`End of function: x.value = ${x.value}`);
}

const count = { value: 0 };
console.log(`Before: count.value = ${count.value}`);
increment(count);
console.log(`After: count.value = ${count.value}`);

The output of this code is as follows. Note that the value of the count.value property is changed after the function call. This makes it seem like objects are passed by reference in JavaScript, but they are not!

Before: count.value = 0
End of function: x.value = 1
After: count.value = 1

Horstmann explains:

“As you have seen, it is easily possible—and in fact very common—to implement methods that change the state of an object parameter. The reason is simple. The method gets a copy of the object reference, and both the original and the copy refer to the same object.

“Many programming languages (in particular, C++ and Pascal) have two mechanisms for parameter passing: call by value and call by reference. Some programmers (and unfortunately even some book authors) claim that Java uses call by reference for objects. That is false. As this is such a common misunderstanding, it is worth examining a counterexample in detail.”

Object references are passed by value

Horstmann’s counterexample is a method that attempts to swap the values of two variables that are assigned to objects. Here is a similar example in JavaScript.

function swap(x, y) {
[x, y] = [y, x];
console.log(`\nEnd of function: x = ${x.name}`);
console.log(`End of function: y = ${y.name}`);
}

let a = { name: "Tom" };
let b = { name: "Jerry" };

console.log(`Before: a = ${a.name}`);
console.log(`Before: b = ${b.name}`);

swap(a, b);

console.log(`\nAfter: a = ${a.name}`);
console.log(`After: b = ${b.name}`);

The output of this code is as follows. Note that the objects are not swapped after the function call. This proves that object references are passed by value in JavaScript. If they were passed by reference, then the values of the a and b variables would be swapped.

Before: a = Tom
Before: b = Jerry

End of function: x = Jerry
End of function: y = Tom

After: a = Tom
After: b = Jerry

Again, the only way to “make this work” is to return the swapped values of the x and y parameters from the swap(x, y) function and reassign the a and b variables to them.

function swap(x, y) {
[x, y] = [y, x];
console.log(`\nEnd of function: x = ${x.name}`);
console.log(`End of function: y = ${y.name}`);
return [x, y];
}

let a = { name: "Tom" };
let b = { name: "Jerry" };

console.log(`Before: a = ${a.name}`);
console.log(`Before: b = ${b.name}`);

[a, b] = swap(a, b);

console.log(`\nAfter: a = ${a.name}`);
console.log(`After: b = ${b.name}`);

The output of this code is as follows:

Before: a = Tom
Before: b = Jerry

End of function: x = Jerry
End of function: y = Tom

After: a = Jerry
After: b = Tom

Horstmann explains why the original does not work:

“However, the method does not actually change the object references that are stored in the variables a and b. The x and y parameters of the swap method are initialized with copies of these references. The method then proceeds to swap these copies.

“But ultimately, this is a wasted effort. When the method ends, the parameter variables x and y are abandoned. The original variables a and b still refer to the same objects as they did before the method call.”

Call by sharing

An article on Wikipedia titled Value type and reference type explains that this behaviour is sometimes known as call by sharing. Under the heading Reference types and “call by sharing,” it says:

‘Even when function arguments are passed using “call by value” semantics (which is always the case in Java, and is the case by default in C#), a value of a reference type is intrinsically a reference; so if a parameter belongs to a reference type, the resulting behavior bears some resemblance to “call by reference” semantics. This behavior is sometimes called call by sharing.

‘Call by sharing resembles call by reference in the case where a function mutates an object that it received as an argument: when that happens, the mutation will be visible to the caller as well, because the caller and the function have references to the same object. It differs from call by reference in the case where a function assigns its parameter to a different reference; when that happens, this assignment will not be visible to the caller, because the caller and the function have separate references, even though both references initially point to the same object.’

Summary

Horstmann summarizes all of this as follows:

  • A method cannot modify a parameter of a primitive type.
  • A method can change the state of an object parameter.
  • A method cannot make an object parameter refer to a new object.