Have you ever encountered an error like this while dealing with JavaScript Arrays?
Note: Uncaught TypeError: children.forEach is not a function
forEach is surely a function of an Array, then why do we get an error like the above? There are a few possibilities,
- You may not be using forEach on an Array at all. By mistake, you may be using forEach on a plain JavaScript object, or a string, etc.
- You may be using forEach on an Array-Like object, which you assumed as an array but, it is not.
In this article, we will learn about JavaScript array-like objects and how to deal with them. I hope you find it useful.
What are array-like objects?
In JavaScript, objects are used to store multiple values as a complex data structure.
An object is created with curly braces {โฆ} and a list of properties. A property is a key-value pair where the key must be a string, and the value can be of any type.
On the other hand, arrays are an ordered collection that can hold any data type. In JavaScript, arrays are created with square brackets [...], and elements are indexed.
An array-like is an object.
- Has indexed access to the elements and a non-negative length property to know the number of elements in it. These are the only similarities it has with an array.
- Doesn't have any of the Array methods like push, pop, join, map, etc.
Here is an example of array-like object:
If you do:
Array-like is completely different from a normal array. It is not constructed by Array or with an Array literal []. Hence it won't inherit anything from Array.prototype. That's the reason we do not see any of the Array methods in array-like.
The length property will not automatically update as well. You can not shrink the array-like by reducing the length property value you do with arrays.
With ES6, you can check this easily:
Array-like is rather a normal JavaScript object. Even normal Arrays are Objects in JavaScript.
But, why do you need to know about it?
JavaScript programming language has many usages of Array-like objects. You may interpret them as an Array and get into possible bugs if you are not aware. We also need to know how to deal with the Array-like object once we recognize one.
arguments is an Array-like object
arguments is an Array-like object accessible inside functions that contain the values of the arguments passed to that function.
Let's call this function with a couple of arguments:
The output in the browser console:
Did you notice the __proto__ value in the output above? Yes, it is an object, not an Array. Like any Array-like objects, it has a length property, and the values are indexed.
Let's try to use some of the Array methods on the arguments now.
When we try to pop an element of the arguments, we will get the following error:
How about trying out forEach?
No luck! We will get the error:
JavaScript HTMLCollection is an Array-like object
Another example of a JavaScript Array-like object is the DOM HTMLCollection. Methods like the getElementsByTagName() returns an HTMLCollection.
Let's understand it with an example:
Now, let us try to query the DOM using the method, getElementsByTagName(). We will be using the tag li for this example.
The output is:
As you see, it is an HTMLCollection and looks like an Array. Let us expand the value of __proto__ and see what the type of HTMLCollection is?
Did you see that? Yeah, it is also an Object. How about we try forEach on it?
No luck! It is because HTMLCollection is an Array-like object and none of the Array methods are available.
How to deal with an Array-like?
In many situations, you may want to treat an Array-like as an Array. There are some advantages to it. If you can convert an Array-like to an Array, you can use all the array methods for computations. But how to do that?
There are three ways we can accomplish it.
Using ES6 Spread operator
We can use the ES6 spread operator([...array-like]) to convert an Array-like to an Array. Let us revisit the example of the arguments.
We are using the spread operator on arguments and are now allowed to use forEach on it.
Try:
Output:
Use Array.from(array-like)
You can use Array.from(array-like) to concert and Array-like to an Array.
We can do the following for our HTMLCollection example:
If you do console.log(collection), you will find this in the browser console,
Please check the value of __proto__ now. It is an Array.
Using the slice method
In the pre-ES6 era, you can use the slice() method to do the conversion. But wait, isn't the slice() method is from Array? How are we going to use it on an Array-like? Check this out:
A few things are going on there. Let me explain.
- Array.prototype gives us access to all the methods and properties.
- We canโt call the slice() method directlyโthe this keyword point to Array, not the arguments variable.
- call() is the prototype method of the Function object. It allows us to change what the this variable points to inside a function.
In Summary
Let us summarize what we have learned,
- Array-like is not an Array. They have indexed access to the elements and a length property. All the similarities with an Array end here.
- Array-like is just like a normal JavaScript Object.
- JavaScript language has many Array-like objects that you may end up using.
- There are three ways to convert an Array-like to an Array so that you can deal with it properly. Use the spread operator, Array.from or the slice() method.
โ