Is JavaScript call-by-value or call-by-reference?

A:  JavaScript is call-by-value.

Q:  How can that be when a majority of the types in JavaScript are implemented as references?

A:  Both call-by-value and call-by-reference (termed evaluation strategies), have absolutely nothing to do with how variables are represented or communicated within a language. Evaluation strategy instead defines the extent to which a function is able to manipulate the environment from which it was called through the arguments it has received.

Evaluation Strategy

When you call a function, the way arguments are handled has a huge impact on how we, as developers, are able to reason about the state of the environment after a function has been called.

The Wikipedia entry for Evaluation Strategy is long, confusing and sometimes contradictory because the various terms have been inconsistently applied to languages throughout the years.

The most important distinguishing features of call-by-value is:

If the function is able to assign values to its parameters, only its local copy is assigned — that is, anything passed into a function call is unchanged in the caller’s scope when the function returns.

In call-by-value, a function can’t make modifications to the calling environment by direct assignment to the arguments it has received. Everything in the argument list is a copy. Inside the function, any assignment to an argument changes the argument only for the remaining duration of the function body. From the point-of-view of the calling environment, no assignment was ever made.

Compare that to the definition of call-by-reference:

[call by reference] typically means that the function can modify (i.e. assign to) the variable used as argument — something that will be seen by its caller.

How JavaScript Works

The definition for call-by-value is exactly how arguments within JavaScript functions operate. No matter how you attempt to assign a new value to an argument, that change will never be visible from outside the function.

The example below clearly shows this behavior:

var a = 1,
    b = { foo: 'bar' },
    a_ = a,
    b_ = b;

(function (_a, _b) {
    _a = 2;
    arguments[1] = { foo: 'baz' };
}(a, b));

a === a_; //=> true
b === b_; //=> true

There is simply no way to have the conditions at bottom of the snippet evaluate to false in JavaScript without manipulating the closed-over variables directly. It is impossible to alter the value of a function’s arguments in a way that is visible to the calling environment.

What About Objects!?

“Aha!”, says the JavaScript developer, “But I can change objects and that change is visible to the calling environment because objects are passed-by-reference!”

Sorry, this particular behavior does not mean that JavaScript is call-by-reference. This common misconception is the result of conflating the argument’s value for what the argument references.

While it is true to say that JavaScript is passing your function a reference to an object, that reference itself is not the same reference that existed outside of the function. The reference is passed by value – your functions only ever receive a copy of the original references.

In other words, JavaScript treats references to objects just like values and those values are copied just like any other value when a function is called with an object for an argument.

That Other call-by-value Language

Let’s take a moment to look at an unquestionably call-by-value language: C.

C takes a somewhat more strict approach to call-by-value and copies everything including structures and arrays on the stack. Since JavaScript has no direct analogue to C’s stack-based structures or arrays, that particular case isn’t important. In JavaScript, references to objects and arrays behave like C’s pointers to structures and arrays on the heap.

Like everything else in C’s call-by-value semantics, pointers are copied as well. One of the effects of this copying is that changes to a pointer’s value are not visible to the outside world. On the other hand, the memory to which that pointer refers is clearly still mutable and those mutations are visible outside of the function.

The key point here is: no one will ever attempt to argue that C is call-by-reference simply because you are able to mutate whatever region of memory a pointer references. What matters is that the value of the pointer has been copied and that any changes to that pointer’s value itself are not visible to the environment from which the function was called.

Before We Go

There is one more interesting detail to consider. In C, we can emulate a call-by-reference system by passing pointers for value-types and pointers-to-pointers for reference-types (on the heap). In that way it is possible, though quite laborious, to explicitly implement call-by-reference semantics in C.

This particular method of implementing a call-by-reference system is not available to us in JavaScript-land because the language’s internal “reference values” are completely out of the reach of the developer.

The end result is that JavaScript is so strictly call-by-value that it is impossible to implement a call-by-reference evaluation strategy in JavaScript even if one so desired!

2 thoughts on “Is JavaScript call-by-value or call-by-reference?

  1. aeoril

    Nice article, and thanks. It certainly appears to my best estimate to be accurate in toto. I especially like your elucidation of the topic of the definition of call-by-value vs. call-by-reference. Your stance is very reasonable.

    It is interesting to note that in his book ‘JavaScript: The Definitive Guide’, David Flanagan suggests it is helpful to look at two basic types of variables with respect to how they are stored:

    Primitive types (Numbers, Boolean, null and undefined) he suggests are stored directly in the variable in what could be easily be considered a fixed width memory location (say, 8 bytes to cover all the various types). That is to say, the value of the primitive types is stored directly in the storage location allocated for that variable.

    The other basic type he states are reference types (objects, arrays and functions). These objects are not able to fit in a predefined size since they can have any size, and a reference is stored directly as the value of the variable. That reference value then points to the memory location where the actual data resides for these variables.

    Strings do not necessarily fit either category, but since they are immutable, act essentially like primitive types without necessarily needing to be implemented as such by the JavaScript implementation.

    Hence, though the argument value of a reference type cannot be changed within a function since it is copied into the argument, what it points to can be changed within the function:

    var z = {a: 'a'},
    a = z,
    func = function (obj) {
    obj.a = 'inFunc';
    obj = {};
    }
    func(z);
    console.log((a === z) + " " + a.a); // true inFunc

    Thanks for the very helpful article!

    Reply
  2. aeoril

    In the previous comment by me, the last paragraph before the code sample is inaccurate. It should read better. This:

    “Hence, though the argument value of a reference type cannot be changed within a function since it is copied into the argument, what it points to can be changed within the function:”

    Should read this:

    “Hence, though changing the value of the argument within the function has no effect on the variable passed to the function because it is a copy, if that argument is a reference type what it points to can be changed and that change reflected outside the function:”

    Reply

Leave a Reply