JS: Array-Like Object to Array
Convert Array-Like Object to Array
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
xArray.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 arlike.
- By spec, the array method
obj.slice()
works by setting the function's keywordthis
to obj, then goes thru the object's numerical index or properties. - So, that means it will also work with array-like objects.
- The problem is, arlike 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, e.g.
a_array.slice(start, end)
. - How can we call “slice” by
Array.prototype.slice()
yet passing it arlike?
- This can be solved in general using the “call” method from “Function.prototype”.
- Like this
functionName.call(arlike, args)
.
- So, we can do
Array.prototype.slice.call(arlike)
.
- Why can we use
call
fromArray.prototype.slice
butcall
is a method ofFunction.prototype
? - Because
Array.prototype.slice
is a function, so its parent isFunction.prototype
, thus inherits the “call” method. - This is why we can write
Array.prototype.slice.call(…)
.
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.