Array vs Array-like objects in JavaScript

Kirti Singh
Globant
Published in
7 min readSep 16, 2021

The invisible and the non-existent look very much alike. — Author: Delos McKown

Every time we encounter array-like objects, we are in an inquisitive state, and curiosity to understand what lies beneath it led me to write down this blog.
Before exploring the obscure part, we will refresh some basics first,

Object: An object is a non-primitive data type in javascript that we use to store data in a key-value format. The majority of objects in javascript are derived from the Object prototype. There are various object forms in javascript, depending on the acts of accessing and manipulating the properties, expressed as internal methods; As per ECMAScript documentation, an ordinary object requires specific criteria of the default action for the primary internal methods while an exotic object might not have a default action for a few of its internal methods.
NOTE: An exotic object, like an array object or a string object, is not an ordinary object.

Array: In general, it’s a list of numerically indexed values in the order of their entry; In JavaScript, it’s an exotic object which maintains array-index property keys called an element and a non-configurable property called length, and each array is derived from this Array object inheriting all of its properties like push, pop, slice, map, shift, and many more. Similarly, with some behavioral differences, a string is also an exotic object consisting of integer-index properties where each property corresponds to a character.

What is an array-like object?

An object is an array-like if it satisfies the criteria listed below :
a) The index should start at 0
b) The index should be incremented in the same way as that of an array
c) It should have a length property and returns a non-negative integer
d) Its value should be equal to the number of integer-index keys

How are array-like objects different from an array?

Speaking of the difference between array and array-like objects:
a) The index range of an array-like object is 0≤I≤2^53–1, whereas the index range of an array is 0≤ I <2^32–1
b) An array-like object is not derived from the Array.prototype object; therefore, we cannot perform traversing and remodeling operations like push, pop, forEach, and many more

Examples of array-like objects are:
a) Arguments object in a traditional function
b) HTML collection returned from document.querySelectorAll or document.getElementsByTagName

To understand the difference, let’s take a look at the example given below:

//example_no_1
function printArgumentList() {
console.log("Arguments: ", arguments);
console.log("First argument: ", arguments[0]);
console.log("Length of arguments: ", arguments.length);
}
printArgumentList("one", 2, 3);
Output: example_no_1

here, we can locate the elements of the ‘arguments’ object and evaluate its length the same as we do for an array object, but when we try to insert an element, arguments.push("four"), it throws an error “TypeError: arguments.push is not a function” or when we try to iterate over it using forEach it throws “TypeError: arguments.forEach is not a function”.

To understand array-like objects in a better way, we will talk about strings within javascript,

In ECMAScript5, the string was introduced as an array-like object in which every character is accessible using its numerical index. For instance, string[2] will return “r” although the characters are neither writable nor configurable.

So, it indicates we can obtain the characters of a string like it’s an array of characters although we cannot manipulate it; In the below example, an object is defined as a string to illustrate its array-like characteristics,

//example_no_2
let stringObj = {};
Object.defineProperties(stringObj, {
0: {
value: "a",
writable: false,
enumerable: false,
},
1: {
value: "b",
writable: false,
enumerable: false,
},
length: {
value: 2,
writable: false,
enumerable: false,
},
valueOf: {
value: function () {
return "ab";
},
writable: false,
enumerable: false,
},
});
//use_case_1console.log("Before string manipulation");console.log("Primitive value of string object: ",stringObj.valueOf());console.log("String length: ", stringObj.length);console.log("Character at zeroth index: ", stringObj[0]);
Output: example_no_2_use_case_1
//use_case_2
//Editing character at zeroth index
stringObj[0] = “test”;
console.log(" -----After string manipulation----- ");console.log("Primitive value of string object: ",stringObj.valueOf());console.log("String length: ", stringObj.length);console.log("Character at zeroth index: ", stringObj[0]);
Output: example_no_2_use_case_2
//use_case_3
console.log(" -----Adding new character to the string----- ");
stringObj.push("V");
console.log(" -----Convert to uppercase----- ");
stringObj.push("V");
stringObj.push("V");
Output: example_no_2_use_case_3

In the above example, ‘stringObj’ was initialized as an empty object followed by defining its properties. The added properties appear like an array and threw an error on attempting to add a new character.

An array-like object cannot be shaped like a normal array. Let’s try to validate the statement with an example below:

//example_no_3
let str = "JavaScript";
console.log(" ---Add index as a prefix to charcter--- ");console.log("Is str an array: ", Array.isArray(str));str.map((value, index) => `${index}-${value}`);console.log(str);
Output: example_no_3

let’s modify the above snippet to convert ‘str’ to an array,

//example_no_4
str = Array.from(str);
str = str.map((value, index) => `${index}-${value}`);
console.log(str);
Output: example_no_4

now we have the desired output as per the Array.map() function.

A glimpse of few operations on Array and Array-like objects

//example_no_5
let array = ["a", "r", "r", "a", "y"]; //plain array
let arrayLike = { 0: "a", 1: "r", 2: "r", 3: "a", 4: "y", length: 5 }; //array-like object
//use_case_1 - Applying javacript object methods
console.log(array.hasOwnProperty(1), ":", arrayLike.hasOwnProperty(1));
console.log(array.valueOf(), ":", arrayLike.valueOf());
Output: example_no_5_use_case_1
//use_case_2 - Iterating using for-of
for (let value of array) {
console.log(value);
}
for (let value of arrayLike) {
console.log(value);
}
Output: example_no_5_use_case_2
//use_case_3 - call join function
console.log(array.join("*"));
console.log(arrayLike.join("*"));
Output: example_no_5_use_case_3

Calling Array.prototype functions on array-like objects throws an error, but we can still call it with little modification,

//use_case_4 - calling join function for array-like object
console.log(arrayLike, Array.prototype.join.call(arrayLike, “*”));
Output: example_no_5_use_case_4

But it’s not an optimized approach as compared to a normal array. Therefore it’s recommended to convert array-like objects to normal arrays before calling built-in Array.prototype functions.

Ways to convert array-like to arrays

  • Array.from(): it aims to return a new shallow copy of an array-like or iterable object
let str = "Javascript";console.log("Is array before calling Array.from(): ", Array.isArray(str));str = Array.from(string);
console.log(
"Is array after calling Array.from(): ",
str,
":",
Array.isArray(str)
);
function func() {
let arg = Array.from(arguments);
console.log("arguments object: ", typeof arg, " : ", arg);
}
func("One", 1, true);
Output: Converting array-like to arrays using Array.from()
  • Spread operator: it allows iterables to spread their elements whenever zero or more arguments are required. It is expressed by three dots (…). Mostly used to combine two arrays
let str = "Javascript";
console.log("Is array before applying spread operator: ", Array.isArray(str));
str = [...str];
console.log(
"Is array after applying spread operator: ",
str,
":",
Array.isArray(str)
);
function func() {
console.log("arguments object: ", typeof [...arguments], " : ", [
...arguments,
]);
}
func("One", 1, true);
Output: Converting array-like to arrays using spread operator(…)
  • Object.values(): it returns an array containing all enumerable property values of an object. It maintains the order like a loop but excludes properties in the prototype chain
let baseObj = { 0: "Array", 1: "Array-like", length: 2 };console.log(
"Is array before calling Object.values(): ",
Array.isArray(baseObj)
);
let result = Object.values(baseObj);
console.log(
"Is array after calling Object.values(): ",
result,
":",
Array.isArray(result)
);
function func() {
let arg = Object.values(arguments);
console.log("arguments object: ", typeof arg, " : ", arg);
}
func("One", 1, true);
Output: Converting array-like to arrays using Object.values()

Summary

  • An array is an exotic object which has array-index property keys called an element and a non-configurable property called length
  • An array-like object is similar to an array in view but is not derived from the Array.prototype object. Therefore, we cannot apply the array’s traversal and remodeling functions to it
  • The string is treated as an array-like object where each character is accessible using its numerical index and length property return length of the string
  • Array.from(), spread operator, and Object.values() can be used to convert array-like objects to arrays
  • It is a best practice to convert array-like objects to arrays before applying any built-in array functions

We hope it has helped with the confusion between array and array-like objects.
Thanks for staying till the end. :)

--

--