Have you ever encountered an error like this while dealing with JavaScript Arrays?
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 anArray-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 likepush
,pop
,join
,map
, etc.
Here is an example of array-like
object,
// It is like, ['I', 'am', 'array-like']
const arr_like = {0: 'I', 1: 'am', 2: 'array-like', length: 3};
If you do,
arr_like[2]; // returns, array-like
arr_like.length; // returns 3
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.isArray(arr_like); // returns, false
Array-like
is rather a normal JavaScript object. Even normal Arrays are Objects in JavaScript.
arr_like instanceof Object; // returns, true
[] instanceof Object; // returns, true
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.
function checkArgs() {
console.log(arguments);
}
Let's call this function with a couple of arguments,
checkArgs(1, 45);
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.
function checkArgs() {
console.log(arguments.length);// logs 2.
}
Let's try to use some of the Array methods on the arguments
now.
function checkArgs() {
arguments.pop();
}
When we try to pop an element of the arguments, we will get the following error,
How about trying out forEach
?
function checkArgs() {
arguments.forEach((elem) => {
// Do something here...
});
}
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,
<div id="main">
<ul>
<ol type="1">
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
</ol>
</ul>
</div>
Now, let us try to query the DOM using the method, getElementsByTagName(). We will be using the tag li
for this example.
document.getElementsByTagName('li');
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?
document.getElementsByTagName('li').forEach(() => {
// Do something here..
})
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
.
function checkArgs() {
// Using spread operator
[...arguments].forEach((elem) => {
console.log(elem);
});
}
We are using the spread operator on arguments and are now allowed to use forEach on it.
Try,
checkArgs(1,45);
Output,
1
45
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,
const collection = Array.from(document.getElementsByTagName('li'))
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,
const args = Array.prototype.slice.call(arguments);
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βthethis
keyword point to Array, not the arguments variable. -
call()
is the prototype method of theFunction
object. It allows us to change what thethis
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 theslice()
method.
daily.dev delivers the best programming news every new tab. We will rank hundreds of qualified sources for you so that you can hack the future.
Top comments (7)
Thanks very much for this post! I'm new in this world and i try to understand that kind of things and this post help me a lot to understand the logic and see how it works.
Really really thanks!
You made my day! So so glad to know you found it useful.. Very motivating for me as a writer. Thanks!
FYI:
Thanks Taufik for the note. Glad that, you got this into the discussion.
It is because, document.querySelectorAll('li') returns a NodeList, not HTMLCollection.
Here to note, both HTMLCollection and NodeList are Array-like objects. Both have a length property defining the number of items in the list (collection). Both provide an index (0, 1, 2, 3, 4, ...) to access each item like an array.
There is a difference though. You can loop through the node list and refer to its nodes like an array. You can not do the same for HTMLCollection. That is the reason forEach doesn't work in first case and works for second one.
However, you cannot use Array Methods, like valueOf(), push(), pop(), or join() on a node list.
When you try, document.querySelectorAll('li').pop(), you will get Uncaught TypeError: document.querySelectorAll(...).pop is not a function.
We can refer this for further info: w3schools.com/js/js_htmldom_nodeli...
Array-like are objects and not arrays. You can use inbuilt es6 methods to treat them like array of required.
Am I getting it right here?
Hi Varun,
That's right. Array-like objects are not arrays. ES6 has multiple ways to convert them to Array. We can do the conversion pre-es6 ways too. Thank you!
Got it. Thanks.. π