loading...
Cover image for Why do you need to know about Array-like Objects?
daily.dev

Why do you need to know about Array-like Objects?

atapas profile image Tapas Adhikary Originally published at daily.dev ・5 min read

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 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,

// It is like, ['I', 'am', 'array-like']

const arr_like = {0: 'I', 1: 'am', 2: 'array-like', length: 3};

Enter fullscreen mode Exit fullscreen mode

If you do,

arr_like[2]; // returns, array-like
arr_like.length; // returns 3
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

Let's call this function with a couple of arguments,

checkArgs(1, 45);
Enter fullscreen mode Exit fullscreen mode

The output in the browser console,
Alt Text

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.
}
Enter fullscreen mode Exit fullscreen mode

Let's try to use some of the Array methods on the arguments now.

function checkArgs() {
  arguments.pop();
}
Enter fullscreen mode Exit fullscreen mode

When we try to pop an element of the arguments, we will get the following error,

Alt Text

How about trying out forEach?

function checkArgs() {
  arguments.forEach((elem) => {
    // Do something here...
  });
}
Enter fullscreen mode Exit fullscreen mode

No luck! We will get the error,

Alt Text

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>
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

The output is,

Alt Text

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?

Alt Text

Did you see that? Yeah, it is also an Object. How about we try forEach on it?

document.getElementsByTagName('li').forEach(() => {
 // Do something here..
})
Enter fullscreen mode Exit fullscreen mode

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);
  });
}
Enter fullscreen mode Exit fullscreen mode

We are using the spread operator on arguments and are now allowed to use forEach on it.

Try,

checkArgs(1,45);
Enter fullscreen mode Exit fullscreen mode

Output,

1
45
Enter fullscreen mode Exit fullscreen mode

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'))
Enter fullscreen mode Exit fullscreen mode

If you do console.log(collection), you will find this in the browser console,

Alt Text

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);
Enter fullscreen mode Exit fullscreen mode

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.

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.
Daily Poster

Discussion

pic
Editor guide
Collapse
nicolasbiondini profile image
NicolasBiondini

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!

Collapse
atapas profile image
Tapas Adhikary Author

You made my day! So so glad to know you found it useful.. Very motivating for me as a writer. Thanks!

Collapse
taufik_nurrohman profile image
Taufik Nurrohman

FYI:

// :(
document.getElementsByTagName('li').forEach(fn);

// :)
document.querySelectorAll('li').forEach(fn);
Enter fullscreen mode Exit fullscreen mode
Collapse
atapas profile image
Tapas Adhikary Author

Thanks Taufik for the note. Glad that, you got this into the discussion.

document.querySelectorAll('li').forEach(fn); Works

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...

Collapse
varunpappu profile image
Varun Subramanian

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?

Collapse
atapas profile image
Tapas Adhikary Author

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!

Collapse
varunpappu profile image
Varun Subramanian

Got it. Thanks.. 😀