Fixing the JavaScript `Array` Class

How to drag JavaScript Arrays into the modern programming world without breaking them

Terry Morse
4 min readJan 30, 2020

JavaScript is an evolving language, with new features recently added to support modern programming concepts like immutable objects, pure functions, and method chaining. This is known as a “good thing”, as these modern concepts make coding easier to write, understand, and debug.

Unfortunately, while JavaScript is evolving, it still needs to support legacy code. As a result, the standard APIs tend to use a jumbled mess of programming styles, with different patterns used within a single API.

The standard JavaScript Array class has one such messy API. But before delving into its quirks, let me briefly mention some of the “good” modern programming concepts that it ought to be supporting.

Immutable objects

Immutable objects make it easier to write code that doesn’t have undesirable side effects. An immutable object is never modified after it is created. Whenever you want to modify an object, you must create a new one. Example:

const name = 'terry';
const nameCapitalized = capitalize(name);
// name: 'terry', nameCapitalized: 'Terry'

Since name is immutable, we know that capitalize() didn’t change it. This is a good thing, as we no longer have to worry that modifying name will have an effect on some other code that uses that object.

Pure functions

A pure function always returns the same result for the same inputs, and it has no side effects. It simply takes in data and returns data. Here’s a pure function that operates on an array:

const array1 = [1, 2, 3];
const array2 = double(array1);
// array1: [1, 2, 3]
// array2: [2, 4, 6]

In this example, double() receives an array and returns a new array with each of its element doubled. Since it’s a pure function, it did not change the input array (no side effects 👍). It also returned an array that can be further processed, which supports another useful programming concept:

Method chaining

Method chaining is the handy practice of chaining together multiple object functions (methods) to produce a new result. Method chains look something like this:

xNew = x.methodA().methodB().methodC();

Because the double() function in the example above returns an array, it can be chained with any array method. Let’s say we want to convert every array element with this formula: x’ = (2 * x)² . With method chaining, we can do this:

// x' = (2 * x)^2 for each element
const array1 = [1, 2, 3];
const array3 = double(array1).square();
// array3: [4, 16, 36]

double() returns an array, which is then input to square(), which makes a copy of that array, squares every element, and returns the array. All of this is done in a single, compact, easy to read and understand line. This is the power of method chaining.

Problems with the standard JavaScript Array class

Array has been around since the beginning of JavaScript, and it’s used by virtually all programs. So what’s wrong with it?

In a word, plenty. Some of its built-in methods support modern concepts, others don’t. Some methods leave source arrays unchanged (immutable objects 👍), others change arrays in-place (mutable objects 👎). Some methods are pure functions (no side effects 👍), others are un-pure (side effects 👎). In addition, some methods that should return an array don’t (no method chaining 👎).

In addition, the inconsistency between Array methods means the weary programmer must try to remember the peculiar behavior of each method. And since programmers are human, that means more bugs.

In total, Array has 35 methods:

  • 11 modify an array
  • 10 return an array (method chaining 👍)
  • 4 don’t return an array when they should (no method chaining 👎)
  • 6 don’t modify the source array (immutable objects 👍).
  • 4 modify arrays in-place (mutable objects 👎).

If only there were a way to convert these inconsistent and un-modern Array methods into pure functions that maintain object immutability and return arrays, without breaking legacy code that relies of the peculiarities of those methods.

Well, I think there is.

The Fix: ArrayI

To fix the bad parts of Array while keeping the good parts, I’ve created a new array class called ArrayI (I stands for immutable) which inherits all the Array methods.

But here’s the “good thing”: ArrayI replaces all the Array “un-pure” methods with pure ones. Its methods never modify source arrays in place, preserving object immutability. Methods return ArrayI arrays where appropriate, so method chaining works.

ArrayI vs. Array

Here’s a simple example using the copyWithin() method, first using Array:

const myArray = new Array('a', 'b', 'c', 'd', 'e');
const newArray = myArray.copyWithin(0, 3, 4);
// myArray: [ 'd', 'b', 'c', 'd', 'e' ]
// newArray: [ 'd', 'b', 'c', 'd', 'e' ]

Notice that copyWithin() modified myArray—not good! Now the same example using ArrayI:

const myArrayI = new ArrayI('a', 'b', 'c', 'd', 'e');
const newArrayI = myArrayI.copyWithin(0, 3, 4);
// myArrayI: [ 'a', 'b', 'c', 'd', 'e' ]
// newArrayI: [ 'd', 'b', 'c', 'd', 'e' ]

The ArrayI copyWithin() method didn’t modify the source array, maintaining the pure function/immutable object practice.

Download ArrayI

ArrayI is implemented in a module named array-immutable, which may be downloaded from GitHub or npmjs.com.

Or simply download using npm:

$ npm install array-immutable

If you try it out, please let me know what you think of it. Hopefully, it will make your handling of arrays a little easier and more bug free.

--

--

Terry Morse
0 Followers

President, Terry Morse Software, Inc.