An Array By Any Other Type
Type Checking Arrays in JavaScript
2021 is the twentieth anniversary of Douglas Crockford declaring JavaScript as the world's most misunderstood programming language. JavaScript is a complex language, with many follies; however, an under-represented subject in his article is type checking. Type checking is a known pain-point in JavaScript.
The Blunders of typeof
console.log(typeof null); // -> object
typeof null
is a known design error1 that has existed too long to fix.
console.log(typeof []); // -> object
Is this another design error? No. Though this does break the rule of least astonishment, and suggestions have been made to change it, it isn’t considered a design error.
Why is the type of an array an object?
JavaScript consists of two sets of types: primitive values and objects.
What are Primitive Values?
A primitive value can be defined as anything which is not an object, is immutable, and has no methods2. These are: string, number, bigint, boolean, undefined, symbol, and null.
What are Objects?
Paraphrasing from JavaScript.info, an object is a keyed collection of data.
What are Arrays?
Arrays extend objects and are ordered collections of data; the key in our collection is an index. Arrays exist purely for convenience, providing implicit ordering and a flurry of helper methods.
Let's go back to the section title: Why is the type of an array an object?
It's defined to — ES5.1 and previous lists a type table stating that if it's an Object and does not implement the internal property [[Call]]
the typeof will resolve to Object.
Pre-ECMAScript 2015
Before the introduction of Array.isArray
there were two common methods3 of checking if a variable was indeed an array.
instanceof
operator / constructor
property:
let arr = [];
console.log(arr instanceof Array); // true
// likewise
arr = [];
console.log(arr.constructor === Array); // true
There are a few nuances which make the above examples less than ideal.
It Looks like an Array, and Quacks like an Array (Duck Typing)
// Prototype.js (v. 1.6.0.3):
function isArray(object) {
return object != null && typeof object === "object" &&
'splice' in object && 'join' in object;
}
The above example of duck typing is also easy to trick. Any object with a key of splice
and join
will return true.
Object.prototype.toString - Duck Typing through internal properties.
Note: The current ECMAScript definition for Object.prototype.toString
explicitly uses an isArray
check.
A suggested patch in dojotoolkit's caveats provides an alternative for its internal use of instanceof
5, introducing Object.prototype.toString
:
console.log(Object.prototype.toString.call('')); // -> [object String]
console.log(Object.prototype.toString.call(1)); //-> [object Number]
console.log(Object.prototype.toString.call([])); // -> [object Array]
How? Up to ES5.1 every object defined internal semantics including an internal [[class]] property. Object.prototype.toString
references the property.
This makes the implementation of isArray
a string comparison against the return value.
function isArray(object) {
return Object.prototype.toString.call(object) === '[object Array]';
}
Typing and the future
Since ES5 Array.isArray
has alleviated our woes of typing arrays so much that there are talks of further implementations of the Type.isType
style6.
Node has a util.types
API which avoids the changeability of JavaScript, with the overhead of calling into C++.
- ^1 Design Errors is a heading mentioned in the article.
- ^2 Primitive values have wrapper objects that provide the functionality we're use to seeing in JavaScript.
- ^3 In the non-computer-science definition, i.e. ways of accomplishing something.
- ^4 I'm sure this is one of the reasons that Crockford considers JavaScript the world's most misunderstood language.
- ^5 It also references the aforementioned instanceof frames problem.
- ^6 The implementation of some built-in constructors have
isType
methods: e.g.Buffer
'sBuffer.isBuffer
method.
Acknowledgements
Photo thanks to Daniel Salgado on Unsplash.
This article was inspired by a comment from Kyle Simpson on LinkedIn (must be logged in to see comment).
Some of this article was adapted from instanceof
Considered Harmful by kangax.
A majority of this article references ES5.1 as it's more relevant to JavaScript's pre-isArray implementation.
Edits:
10/18/2021: update article url for instanceof
Considered Harmful from previous archive.org link
10/22/2021: various grammatical, punctuation, and spelling fixes. No content changes.