Reference types in JavaScript

19 August 2018

The distinction between reference and value types typically belong to traditional compiled languages such as C# and Java.

There is no definition of value vs reference in the spec of languages like JavaScript and Go. These languages do not wish to make an explicit distinction between references and values. However, they still have types that semantically behave like reference and value types.

Objects have reference type semantics

As extensively discussed elsewhere, the difference between reference and value types boils down to their identity and copy semantics: reference types instances are only self-identical (different value type instances can be identical to each other), reference type instance copy preserves reference identity (value type instance copy does not).

Effectively, all types in the ECMAScript spec have value type semantics except for the object type (and maybe Symbol`s). So, even if the distinction is not emphasized or explicitly stated, it is implicitly part of the language.

Additionally, there is no way to define custom types with value type semantics, any user defined type has reference type semantics (even if defined with no linked prototype via Object.create(null)), anything that isn’t a language primitive (that is has special language syntax support) has reference type semantics. Again, this is unlike languages like C#.

Adapting the trySomething pattern to JS

A typical C# coding patterns is the “trySomething” pattern:

if (!trySomething(out var initializeMe)) {
    throw new Exception();
}

Log("Initialized reference with value:", initializeMe);

This pattern allows attempting some operation with a returned result value that may fail without involving exceptions as a way to communicate failure, compare to the following:

object thing;

try
{
    thing = doSomething();
}
catch (Exception)
{
    throw new Exception();
}

Log("Whew, finally got the:", thing);

Because JS has no support for “pass by reference” semantics this pattern can only be implemented in a slightly awkward roundabout way by passing an object reference and reading a property off of it:

const byRef = {};
if (!trySomething(byRef)) {
    throw new Error();
}

console.log("Initialized property with value:", byRef.something);

Another possible, perhaps less awkward way to implement the pattern in JS leverages object decomposition (this was Luke’s idea):

const {success, value} = trySomething();

if (!success) {
    throw new Error();
}

console.log("Initialized value:", value);

This is more similar to other patterns in JS, such as iterators/generator functions:

const it = someArray[Symbol.iterator];

let done, value;
while(!done) {
    {done, value} = it.next();
    console.log(value);
}

Conclusion

One of the best things about working full stack with different programming languages is the exposure you get to different patterns and language features.

Languages such as JS do not explicitly distinguish between reference and value types, do not allow user defined value types and they do not allow for pass by reference semantics. Lacking these features, alternative patterns have emerged that are roughly equivalent and read with the same level of expressiveness as the originals.

Further reading