img by Mareta Mara

Explaining arrays in JS (common quirks and misconceptions)

Grzegorz Zadora
codequest
Published in
9 min readJun 9, 2020

--

When you learn how to use arrays you will become a powerful developer. Some weird problems may stand on your way, though. So I decided to explain the general concepts of the array and some quirks which cause headaches even among experienced JS devs. I hope it will help you understand arrays better!

What is an array?

An array is a commonly used data structure. In simple words, it’s an ordered list of elements (values or variables), where each one is identified by a positive integer or zero (the first item’s index is always zero). However, you should be aware that JavaScript does not restrict you to this definition (see “Array in JavaScript is mutable” section). An array is great for storing a collection of items of any kind. You might want to use it to create a list of to-dos or employees in your company. Furthermore, it makes executing repeatable tasks really convenient, which allows us to solve problems such as sorting or finding a specific item in the collection.

How to create an array?

There are multiple ways of creating an array in JavaScript, here are some examples that come to my mind at the moment:

A convenient way to check the type of a particular value or variable is by using typeof keyword:

However, when you try to use it on an array, a strange thing happens:

That’s because, under the hood, an array in JavaScript is indeed an object. It's not immediately obvious though, because you can create it using an array literal (square brackets).

Array in JavaScript is mutable

Programmers use the word “mutable” to sound smarter but the concept is simple: if something is mutable it can be modified. As mentioned above, JavaScript treats every array as an object. It is possible to add custom properties to arrays, just as with plain objects. It’s strongly discouraged but you could do that.

By doing so you should not expect built-in array methods and properties to work correctly, though. It is nobody’s fault that you messed up your array ¯\_(ツ)_/¯.

To avoid further modifications on the array you might want to use the Object.freeze function on it. It will, however, make it impossible to use methods like .pop or .push. Also, it still allows you to modify objects inside the array so if you are really concerned about the mutability, check out Immutable.js library.

Length of an array is not fixed

That means when you create an array you don’t care about its length. You might create an array which initially contains only four elements, but it’s not a problem to add more elements or delete some later. While you are adding or removing items from the array JavaScript automatically changes its .length property. It works properly only when you’re using standard built-in array methods such as .pop or .shift. When you try to delete an array element manually using the delete operator, .length won’t be updated:

Length of an array is not infinite

You should remember that an array’s length is not unlimited and a RangeError might occur when your array gets massive. The maximal size of an array is 4,294,967,295 elements (according to ECMA-262, 10th edition, tested on Chrome 80):

An array can contain different data types

For example, consider the following code:

In the above snippet, you can see an array that contains: a string, a number, and a function that returns a string. Nothing can stop you from adding different data types to a single array. That doesn’t mean you should do that, though. In my opinion, it might make your code confusing. People might not expect strings, integers, and functions in the same array. However, if it’s really necessary, you have that capability.

Multidimensional arrays

Sometimes it’s not so obvious how to create and access elements of multidimensional arrays in JavaScript. To create a multidimensional array you need to nest arrays — one inside another. Basically you’re creating an array of arrays.

Let’s say you want to create a tic-tac-toe game. You need to implement a two-dimensional board. One way to approach the problem is by using a two-dimensional array, in JavaScript, you would do the following:

Spread operator

It’s syntactic sugar which can replace .from and .concat methods. It allows us to copy or merge arrays:

Another use case is to create an array from a string

It might also be useful when you want to pass all elements of an array as arguments to a function (that’s really useful in my opinion):

Destructuring assignment

We can grab some items from an array and immediately assign them to variables:

Also, we can combine it with the spread operator:

Comparing arrays

Comparing arrays is not so simple:

So what can we do here? We can implement a simple comparison ourselves:

It’s a naive solution and it works only for primitive values because we are executing shallow comparisons here (we are comparing by value). It will not work when we have non-primitive values in the array (e.g. objects). In this case, you need to implement a deep comparison. It’s not a trivial task, though. In such cases, to avoid reinventing the wheel, I recommend using isEqual from the lodash library.

Passing an array to the function

When you pass an array as a function argument you might obtain bizarre results. Look at the snippet below:

Hm, the array has been changed inside the function… What can we do about it? Maybe we can assign the array to a local variable inside the function? Let’s try it…

Still, we have the same result… That’s because when we deal with an object (and as I previously mentioned, the array is one of a special kind), the variable only keeps a reference to that object, not the object itself. So, regardless of whether we pass a variable to the function or use a local variable inside of it, we’re still dealing with the same array. What we need to do is create a copy of the array, so that we can safely work on it inside the function and still have the original array outside the function.

Let’s try it:

It works! I’m aware this part might be confusing. To understand it deeply please look up: Value vs. Reference in JavaScript.

Mutating the array

It’s not good practice to manipulate an array directly because it will be difficult to track the changes we’ve made to it, either for the JS engine and people who read the code. Instead, we should use array mutating methods. Most of them are pretty straightforward so I’m not going to cover all of them in details, but rather just give a quick overview:

  • [].pop - removes the last element from an array and return this element
  • [].push - adds one or more elements to the end of an array and returns the new length of the array
  • [].unshift - adds one or more elements to the beginning of an array and returns the new length of the array
  • [].shift - removes the first element from an array and returns this element
  • [].reverse - reverse the order of the elements in the array

More knowledge is available on the MDN. However, there is one method that is not so simple to understand and I’d like to discuss it…

[ ].splice(start, deleteCount, …items)

start — the position from which you want to start removing items

  • If it’s greater than array length, it is set to be equal to the array’s length and no items will be deleted
  • If it’s smaller than 0 it will start from the position = array.length — start
  • If array.length + start < 0 it is set to 0

deleteCount — stands for the number of items you want to delete

  • If is omitted or greater or equal to array.length, then all of the array elements are removed

…items — after start and deleteCount you can put (as many as you want) values which you want to add to the array after the start position

Splice returns an array of items which have been removed from the calling array or empty array if none of the items were removed. You can use it when you want to add, delete or replace items at a specific position in the array

There is a big chance that you’ve heard that it’s good practice to ensure that a function does only one thing and does it well. Splice doesn’t fit into this rule. You can use it for two opposite things: adding and deleting elements to and from an array, even simultaneously. Furthermore, this method’s name is almost identical to .slice, despite the fact that they work in a completely different way. It’s also important to remember that this method always returns an array (even if just one item has been removed or none of the items have been removed). Some use cases:

  • Remove element at a specific index:
  • Add element at a specific index:
  • Replace element at a specific index:

Iterating over array elements

What makes arrays really useful is that we can easily iterate over their elements. That allows you to do repeatable tasks with a small amount of code. There are many ways of doing that so it might be a little bit confusing. Let’s see how they differ:

  • Classic “for” loop
    Mostly used by the beginners since it’s described in most of the programming books. In the example below, you can see how to console.log each element of the array using for loop.

It’s a pretty straightforward approach. It has some problems, though:

  • It’s not so convenient to declare a variable for an index, a condition, and an action each time you want to iterate over something. In bigger code bases it would be repeated a lot.
  • It leaves room for so-called “off-by-one” errors. Sometimes people might not be sure if it will repeat 2 times, or 3 times or 4 times… Maybe someone accidentally put <= instead of <.

It might not sound like a big deal, but you know — code smells…

  • For…of
    A better version of the above. You don’t have to declare a variable and a condition, so you won’t get off-one errors anymore.
  • For…in loop
    The problem with the previous approach occurs when you need an element’s index for some reason. Then you can use for..in loop which is very similar, but in each iteration, you’ll get an element’s index instead of the element itself.
  • Array iteration methods
    For dessert, we have array iteration methods. There are a few of them and each one is slightly different, but the rule is the same for all: you put a function (usually called a callback) as the argument of the iteration method and that function will be called for each element of the array. It’s a more sophisticated way of iterating over array elements than previous ones in my opinion. Since there are iteration methods for different cases you make your intent more clear for the reader. What’s also good, you’ll always get at least two parameters in the callback function: the current element and its index.

Let’s try the most basic one, forEach

There is one gotcha with this approach, however:

Why can’t we iterate over the array created using the constructor?

As you’ve seen above forEach (as well as map, filter, and other iteration methods) won’t work with the array created using the constructor. Let’s do a little investigation. First. let’s see what’s inside array created using literal:

The result will be:

If you take a look at the array created using constructor you can see something different:

The result will be:

It seems that the current array does have the desired length, although it doesn’t have any elements. Since it doesn’t have any elements there is nothing to iterate through. The iteration method has been designed to iterate over existing array indexes. That way we can avoid running unnecessary iterations on nonexisting items.

So the easy fix for the constructed array will be for instance using .fill method:

Summary

When you are aware of some counter-intuitive features of JavaScript, you can avoid most of the common issues. You might wonder: why won’t we fix those parts of the language in its further versions? It’s not so easy. We have to support legacy code and that’s why we cannot just change or remove anything we want from the language… However, if you remember about those little quirks, you can save yourself a lot of headaches.

--

--