Search code examples
javascriptprototypeprototypal-inheritance

How does the Array.isArray() method check for array?


Scope: I have been learning prototype inheritance in java script and I tried the following code in my web browser. I found something really interesting while executing the following code.

let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};
const p=Object.create(rabbit)
rabbit.__proto__ = animal
let y=new Array(2,3)
console.log(Object.getPrototypeOf(y))
var val=y.find((b)=>(b==2));
console.log(val);
console.log(Array.isArray(y))
y.__proto__=rabbit
console.log(Array.isArray(y))
console.log(Object.getPrototypeOf(y))
var ccl=y.find((b)=>(b==2)); 

Question: I have created a array and printed its prototype and then called the find method of array and check whether it's an array using Array.isArray() method. Then I have assigned the prototype of y to rabbit object.

When I checked if it is still an array, I got true, but when calling the find method I get an error and on checking the prototype there was no functions of an array.

Then on what basis does the Array.isArray() method returns true.

Output:

[constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]at: ƒ at()concat: ƒ concat()constructor: ƒ Array()copyWithin: ƒ copyWithin()entries: ƒ entries()every: ƒ every()fill: ƒ fill()filter: ƒ filter()find: ƒ find()findIndex: ƒ findIndex()findLast: ƒ findLast()findLastIndex: ƒ findLastIndex()flat: ƒ flat()flatMap: ƒ flatMap()forEach: ƒ forEach()includes: ƒ includes()indexOf: ƒ indexOf()join: ƒ join()keys: ƒ keys()lastIndexOf: ƒ lastIndexOf()length: 0map: ƒ map()pop: ƒ pop()push: ƒ push()reduce: ƒ reduce()reduceRight: ƒ reduceRight()reverse: ƒ reverse()shift: ƒ shift()slice: ƒ slice()some: ƒ some()sort: ƒ sort()splice: ƒ splice()toLocaleString: ƒ toLocaleString()toString: ƒ toString()unshift: ƒ unshift()values: ƒ values()Symbol(Symbol.iterator): ƒ values()Symbol(Symbol.unscopables): {copyWithin: true, entries: true, fill: true, find: true, findIndex: true, …}[[Prototype]]: Object
2
true
true
{jumps: true}jumps: true[[Prototype]]: Objecteats: true[[Prototype]]: Objectconstructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__()__lookupSetter__: ƒ __lookupSetter__()__proto__: (...)get __proto__: ƒ __proto__()set __proto__: ƒ __proto__()
Uncaught TypeError: y.find is not a function
    at <anonymous>:17:11

Solution

  • Depends on who you ask.


    The ECMAScript 2021 Language standard says:

    7.2.2 IsArray ( argument )

    The abstract operation IsArray takes argument argument. It performs the following steps when called:

    1. If Type(argument) is not Object, return false.
    2. If argument is an Array exotic object, return true.
    3. If argument is a Proxy exotic object, then
      a. If argument.[[ProxyHandler]] is null, throw a TypeError exception.
      b. Let target be argument.[[ProxyTarget]].
      c. Return ? IsArray(target).
    4. Return false.

    (Reformatted for this platform)

    It may help to know that built-in Javascript objects have a representation that is internal to the engine. This is what the above pseudo-code is referring to. Javascript doesn't have access to some of the things that the pseudo-code refers to but the engine does.


    Javascript libraries, on the other hand, typically do one of several things depending on how old the library is, how robust they want to be, or what their goal is. Since they don't have access to the engine internals, these are guesses with a high probability of being correct in the real world:

    • Modern libraries defer to Array.isArray()
    • Polyfills typically use Object.prototype.toString.call(obj) === '[object Array]' (standardized by Mozilla, I believe)
    • Duck typing: Boolean(obj) && obj.length != null && typeof obj.push === 'function'
    • Constructor based: Boolean(obj) && obj.constructor === Array
    • instanceof based: obj instanceof Array

    This is not an exhaustive list.