I can easily make a plain object look like an array by setting its prototype to Array.prototype
:
const obj = {};
Reflect.setPrototypeOf(obj, Array.prototype);
(I'm aware that there are also some problems with the magic length
property and sparse arrays, but that's not the point of this question.)
I want to make Array.isArray(obj)
return true
(of course without modifing the Array.isArray()
method). The MDN polyfill for Array.isArray()
is as follows:
if (!Array.isArray) { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; }
By using the Symbol.toStringTag
property I can make Object.prototype.toString.call(obj)
return '[object Array]'
:
obj[Symbol.toStringTag] = 'Array';
console.log(Object.prototype.toString.call(obj) === '[object Array]'); // true
Now the polyfilled Array.isArray()
returns true
for obj
(please ignore the fact that none of the browsers that doesn't support Array.isArray()
does support Symbol.toStringTag
). However, the native Array.isArray()
function still returns false
for obj
. I looked at the ECMAScript 2017 specification and it says that Array.isArray()
uses the abstract operation IsArray
, which returns true
if the argument is an Array exotic object. If the argument is a Proxy, it calls IsArray
directly on the target object, so it seems that using a Proxy wouldn't help here.
Is there any way to make Array.isArray(obj)
return true
? To make it clear, I don't want to modify Array.isArray()
or any other build-in objects.
This is basically the same question as Can you fake out Array.isArray() with a user-defined object?, but it was asked 5 years ago, and the answers are based on the ECMAScript 5 specification. I'm looking for an answer based on the ECMAScript 2017 specification.