JS: Array-Like Object to Array

By Xah Lee. Date: . Last updated: .

JS2015 or After: Convert Array-Like Object to Array

Use Array.from

Before JS2015: Convert Array-Like Object to Array

Here is how to convert array-like object to array using pre-JS2015 code.

Array.prototype.slice.call(ArrayLikeObject)

/*
 convert array-like object to array, pre 2015 compatible code
*/

// create a array-like object
const aa = { 0: "a", 1: "b", 2: "c", length: 3 };

// convert to array
const bb = Array.prototype.slice.call(aa);

console.log(Array.isArray(aa) === false);

console.log(Array.isArray(bb));

console.log(aa); // { '0': 'a', '1': 'b', '2': 'c', length: 3 }
console.log(bb); // [ 'a', 'b', 'c' ]

How does this work?

The array method arrayX.slice() with no argument will just make a shallow copy (and returns a array). We want to use this method on our array-like object alo.

[see Array.prototype.slice]

By spec, the array method obj.slice() works by setting the function's keyword this to obj, then goes thru the object's numerical index or properties. So, that means it will also work with array-like objects. [see Array-Like Object] [see this Binding]

The problem is, alo isn't a array, so it doesn't inherit method “slice” from array object. What to do?

Array methods are properties of Array.prototype. So, “slice” can be accessed by calling Array.prototype.slice().

The problem is, slice() doesn't take a object as argument. Its only args are start/end indexes, for example: a_array.slice(start, end). How can we call “slice” by Array.prototype.slice() yet passing it alo?

This can be solved in general using the “call” method from “Function.prototype”. Like this functionName.call(alo, args).

So, we can do Array.prototype.slice.call(alo). [see Function Call, Apply, Bind]

Why can we use call from Array.prototype.slice but call is a method of Function.prototype? Because Array.prototype.slice is a function, so its parent is Function.prototype, thus inherits the “call” method. This is why we can write Array.prototype.slice.call().

Here is a fuller explanation with code.

// the object 「Array」 is a standard object, and is a function object
console.log(
  typeof Array === "function",
);

// Array has a property key "prototype"
// every function has a property key "prototype", by spec.
console.log(
  Object.prototype.hasOwnProperty.call(Array, "prototype"),
);

// the value of Array.prototype, is a object, and the only way to express it is just Array.prototype

// This object Array.prototype, is the parent of array data type object, by spec.
console.log(
  Object.getPrototypeOf([]) === Array.prototype,
);

// This object Array.prototype has a property key "slice"
console.log(
  Array.prototype.hasOwnProperty("slice"),
);

// its value is a function
console.log(
  typeof Array.prototype.slice === "function",
);

// the parent of Array.prototype.slice is Function.prototype
console.log(
  Object.getPrototypeOf(Array.prototype.slice) === Function.prototype,
);

// Function.prototype has a property key "call"
console.log(
  Function.prototype.hasOwnProperty("call") &&
    (Object.getOwnPropertyNames(Function.prototype).indexOf("call") >= 0),
);

// so when eval Array.prototype.slice.call, it's actually calling the value of the property key "call" of the object Function.prototype. Because, the property is in the prototype chain.

// what the 「‹f›.call(‹obj›)」 do is to call ‹f› with ‹f›'s 「this」 value set to 「obj」

To really understand it, read the following in order.

JavaScript, Array-Like Object

BUY ΣJS JavaScript in Depth