What are Array‑Like Objects in JavaScript?

In the world of front‑end development and JavaScript, we developers often encounter structures that resemble arrays, yet aren't true arrays. These are called "array‑like objects", and understanding these unique constructs is crucial for effective JavaScript programming.
What are Array‑Like Objects?
An array‑like object exposes a length and indexed values, but it is not necessarily an Array. It does not inherit the full set of methods from Array.prototype, which is a prototype object, not a constructor. Some array‑like types provide their own methods: for example, modern NodeList objects have forEach().
Common Examples in JavaScript
NodeList: Returned by methods likedocument.querySelectorAll.arguments: An object accessible within functions representing the arguments passed to that function.
Array‑Like Objects in Other Languages
As you might imagine, the concept of array‑like objects isn't unique to JavaScript. Many programming languages have similar concepts, where structures behave like arrays but aren't arrays in the strictest sense. For example, in Python, range() generates a sequence that is array‑like.
Working with Array‑Like Objects in JavaScript
Example: NodeList
When you use document.querySelectorAll, it returns a NodeList:
const divs = document.querySelectorAll('div');
console.log(divs.length); //=> a number that represents the number of div elements within the documentHere, divs is a NodeList, which is an array‑like object, meaning you can access elements with their index (e.g., divs[0]), and you can read its length via .length. Relatively recent updates to the DOM standard even allow NodeList to be directly iterated over using forEach(). This wasn't always the case.
A NodeList does not provide every array method. Convert it with Array.from() or, where it is iterable, array spread if you want a normal array. Conversion is not always required: generic methods such as Array.prototype.map.call(divs, callback) can operate on its indexed values and length directly.
arguments and the Rest‑Parameter Alternative
An ordinary function has an array‑like arguments object containing the values passed to it. Arrow functions do not have their own arguments. For new code, I usually prefer rest parameters: they gather the values into a real array from the start. This TypeScript example uses that alternative:
const sum = (...args: number[]): number =>
args.reduce((sum, current) => sum + current, 0);
console.log(sum(1, 2, 3)); //=> 6Here, ...args creates a real array, so reduce() is available directly. It illustrates the rest‑parameter alternative, rather than an arguments object.
Converting Array‑Like Objects to Arrays
Unsurprisingly, one of the common tasks when dealing with array‑like objects in JavaScript is converting them into 'actual' arrays, thus allowing us access to use all of the array methods on our data (like map, filter, reduce, etc.) which might otherwise be unavailable on this type of structure.
Array.from() accepts an iterable or an array‑like object. Array spread requires an iterable; having indexed properties and a length alone is not enough.
Using Array.from()
Array.from() creates a new, shallow‑copied Array instance from an array‑like or iterable object. It's a straightforward and readable way to perform the conversion.
A Set is an iterable object that can be converted to an array using Array.from(). This is useful when you need array functionalities on a Set, for example:
const aSet = new Set([1, 2, 3, 4, 5]);
const anArray = Array.from(aSet);
console.log(anArray); //=> [1, 2, 3, 4, 5]Using the Spread Operator
Array spread reads an iterable's iterator. For example, const anArray = [...aSet]; works for a Set. But Array.from({ 0: "a", length: 1 }) returns ["a"], whilst spreading that plain object throws a TypeError because it has no iterator.
However, suppose you have two NodeList objects (which are array‑like), and you want to merge them into a single array. This can also be done efficiently using the spread operator, combining them and converting them at the same time:
const divs = document.querySelectorAll('div');
const paragraphs = document.querySelectorAll('p');
const mergedArray = [...divs, ...paragraphs];
console.log(mergedArray); //=> Array of div and p elementsWhen to Use Which Method
Both approaches are useful, but first check the input. Array.from() handles plain array‑like objects as well as iterables; array spread handles iterables. Then consider readability and whether you also need a mapping step.
Readability
It may come down to personal preference, but I feel that Array.from() can be more readable, especially when you have relative juniors working on your codebase.
Performance
Both approaches allocate a new array. Performance depends on the input, engine and work performed during conversion; there is no general winner merely because Array.from() is a dedicated method. Measure if this conversion is significant in the application.
Additional Features of Array.from()
This is probably the big one: Array.from() can take a map function as its second argument, allowing for the transformation of elements during conversion. For example:
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const anArray = Array.from(arrayLike, (x) => x.toUpperCase());
console.log(anArray); //=> ['A', 'B', 'C']Challenges with Array‑Like Objects
There are a few aspects of array‑like objects that can present as challenges to developers, particularly those who are a little less experienced:
Confusion:
They look like arrays but don't behave entirely like arrays, leading to confusion, especially for beginners.- Available methods: check the object's own API. Convert to an array when that helps, or borrow a compatible generic array method when conversion is unnecessary.
Performance:
Converting array‑like objects to arrays can have performance implications, especially with large datasets.
The Wrap‑up
Array‑like objects in JavaScript are a unique feature that, whilst powerful, can introduce complexity. Understanding their nature, and knowing how to convert them to arrays when necessary is a key skill in JavaScript development.