Array-like Objects
Greetings, friends! Today, I'd like to talk about a topic you may not hear very often: array-like objects. When working with the Array.from method, you'll see in the documentation that it accepts an arrayLike
parameter or an iterable. What are array-like objects, though? Let's find out!
An array-like object is simply an object that contains indices and a length
property. Obviously, arrays are considered array-like, since they fulfill both criteria.
const arr = ['a', 'b', 'c', 'd', 'e'];
console.log(Object.keys(arr)); // ["0", "1", "2", "3", "4"]
console.log(arr.length); // 5
An array is considered a type of object in JavaScript:
const arr = ['a', 'b', 'c', 'd', 'e'];
console.log(typeof arr); // object
This is why we're able to use Object.keys(arr)
to display the array's keys. Each element in an array represents a value in an object's key-value pair.
Let's try to create our own array-like object:
const iceCreamFlavors = {
0: 'vanilla',
1: 'chocolate',
2: 'strawberry',
3: 'blueberry',
4: 'mango'
}
If we try calling Array.from
on this object, let's see what happens:
console.log(Array.from(iceCreamFlavors));
// OUTPUT: []
We get an empty array. That's because we're missing one key ingredient! No pun intended 😂. We need to add a length property as well:
const iceCreamFlavors = {
0: 'vanilla',
1: 'chocolate',
2: 'strawberry',
3: 'blueberry',
4: 'mango'
}
iceCreamFlavors.length = Object.keys(iceCreamFlavors).length;
console.log(Array.from(iceCreamFlavors));
// OUTPUT: ["vanilla", "chocolate", "strawberry", "blueberry", "mango"]
Ta-da! 🎉 We have created a delicious array-like object! 😋
Where is this knowledge more applicable? It turns out that the document.querySelectorAll method actually returns an array-like object for us! This method returns a NodeList object. It may look like an array, but it doesn't act like one.
If it doesn't quack like a duck, but it looks like a duck, is it really a duck? 🤔 Please excuse my pondering. I'm referencing duck typing in case you're curious 😃.
Let's look at an example. Suppose we have three div elements:
<div id="one">Div #1</div>
<div id="two">Div #2</div>
<div id="three">Div #3</div>
If we run a query selector on all of these divs, we get back an object that looks like an array. However, we can't perform any usual array methods on it such as Array.map:
const divs = document.querySelectorAll('div');
console.log(divs); // NodeList(3) [div#one, div#two, div#three]
console.log(divs instanceof Array) // false
const map = divs.map(el => console.log(el)); // Uncaught TypeError: divs.map is not a function
Just for curiosity's sake, let's see what happens if we try running the forEach
method:
const divs = document.querySelectorAll('div');
divs.forEach(el => console.log(el));
/* OUTPUT:
<div id="one">Div #1</div>
<div id="two">Div #2</div>
<div id="three">Div #3</div>
*/
You may be thinking there's something special about Array.forEach, but in reality, you're actually using the NodeList.forEach method if you try calling forEach
on a NodeList returned by the document.querySelectorAll
method. Please keep this in mind if you're working with NodeLists!
In order to use array methods on a NodeList, we need to convert it to an array using Array.from
. Since it has indices and a length property, it falls under the category of being an array-like object.
const divs = document.querySelectorAll('div');
// Indices ✅
console.log(Object.keys(divs)); // [0, 1, 2]
// Length property ✅
console.log(divs.length); // 3
// Convert to array
const divsArr = Array.from(divs);
// Array methods now work! 🎉
divsArr.map(el => console.log(el));
/* OUTPUT
<div id="one">Div #1</div>
<div id="two">Div #2</div>
<div id="three">Div #3</div>
*/
Conclusion
If you're ever dealing with objects in JavaScript that look like arrays but aren't quite, then you might be dealing with array-like objects. Keep this in mind as you're reading through documentation on various methods in the DOM API.