Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do some array methods rely on the global Array object?

I'm going through the MDN docs on arrays and when we want to test whether or not an object is an array we use isArray(). However, it's usage is very different to most of the other methods. When you use the regular syntax an error pops up:

console.log([1,2,3].isArray());  // TypeError: [1, 2, 3].isArray is not a function

Whereas this does work:

console.log(Array.isArray([1,2,3]))

I don't understand why isArray() (and a couple of other methods) rely upon some global object rather than just being accessible via the object in question. Why do some methods require the global array object?

like image 894
cham Avatar asked Feb 23 '18 22:02

cham


2 Answers

.isArray() is a static method, it exists on the Array "class" but not on each particular instance. Static methods are called like this: Array.isArray(). Object.create() is another example.

Methods like .map or .slice are not static, so they exist on each instance of the Array "class".

like image 198
Boris Lobanov Avatar answered Sep 28 '22 08:09

Boris Lobanov


rather than just being accessible via the object in question.

Because the whole purpose of Array.isArray is to be called on unknown objects. You don't know whether it's an array, you wouldn't know whether the method was accessible on it. The static method even works with values like null, which would inevitably throw an exception when you tried to invoke a method on them.


Digression:

isArray being callable as an instance method can work. In languages where everything is an object, like Smalltalk, Ruby or Io, even things like nil can have a isNil method. This approach has other problems of course, as with dynamic method dispatch every arbitrary object could overwrite the method and claim to be an array - on the other hand, that's exactly what we want for duck typing.

We could even simulate this in JS using Object.prototype.isArray = () => false; and Array.prototype.isArray = () => true;. Apart from failing on null and undefined, it still wouldn't work with objects that don't inherit from (our realm's) Object.prototype. And JavaScript "properties" that mix data fields and methods don't help either (consider the object parsed from the JSON string {"isArray":"maybe"}). We would always have to expect an exception from either .isArray not being a function, or from it being overwritten with a method that throws.
If we want to go for duck typing in JS, checking whether an object has an integer .length property is usually the way to go. Or more advanced, trying to follow the symbol-based iteration protocol. (That's what Array.from uses, for example).


But since arrays are a bit special in JS (with their magic .length property), we want a built-in reliable way to detect them, and that's what Array.isArray does.

Regarding other static Array methods: Array.of is pretty obvious, it's a factory function (like a constructor) and it can't be an instance method because there is no instance to work with in the first place. For Array.from the situation is a bit more like with isArray, a duck-typing Object.prototype.toArray approach could have worked as well but was dismissed for practical and design reasons.

See also Why were ES5 Object methods not added to Object.prototype? and Why is it Object.defineProperty() rather than this.defineProperty() (for objects)? for similar discussions.

like image 24
Bergi Avatar answered Sep 28 '22 07:09

Bergi