Why in JavaScript typeof [] does not return Array
The topic for this entry in my blog is something I discovered on Twitter a while back. Now, my usual boilerplate to my entries that I am certain many are sick of hearing: Yes, I have only basic knowledge of JavaScript, and none of it’s more advanced children like React or Node. For where this post is going we also have to add that I have only intermediate knowledge of the back-end of how these languages actually work. However you see - I do have knowledge. For me it is not enough to know that int number = 13 will produce an integer variable in a Static typed language, and why does var number = 13 do the same in a Dynamically typed? I want to know how it works. I often find myself on a deep dive on why the language works a certain way, and sometimes don’t really feel like I know what I’m doing until I’ve done a couple of those deep dives.
Static vs Dynamic and Strong vs Weak is an important thing to know and something I will probably write another blog post on some day. I’ll just add that to my list of things to do… Oh look at that. That is a very large number of things to do.
Today we look at a classic problem that many people have run into. You want to make sure the variable you’ve just been handed is an array, so in JavaScript code up something that might look like …
And you go about your merry day. When you run your code though your array comes in and never makes it through that check. It always comes back false. Why, you ask? A bit of debugging you find out that typeof [] returns the string “object” rather than “array”.
Confused, you scratch your head. Why does an Array not return as the type of an array? You hit up StackOverflow and discover what you should be doing is ...
You shrug and chalk it up to the vagrancies of some strange developer years ago who decided it must be this way. You move on with your life, filing that little tidbit away for later next time you need to check if something is an array.
That’s not enough for me. I have to know why!
Me? I couldn’t just accept that. I would say “Self! This is crazy” to which I would reply. “You’re talking to yourself, so that’s crazy, but I agree!” Then I would spend way too many hours that should be spent improving my coding diving into old Stack Overflow posts and following arcane links that lead to white pages I can barely understand at best or at worst to defunct links that no longer show what they once claimed to explain. I curse the depth of the internet with the hatred of a thousand burning suns and I cry out for the answer as said hatred consumes me.
Er.
No, not quite. It actually was a pretty quick search, only about thirty minutes.
The answer is really quite intriguing if you care about these things, and well I think it may be obvious that I do.
So? What is it then? Did you figure it out?
An Array is an Object. This we can understand because Object(type) is a prototype object (definition). Confusing yet? In simplest terms an Object is a collection of properties and methods that can be called. array.Length is a property/method, along with other verbage, so that makes an Array an Object.
But I hear you cry, an Array is a specialized type of object. And you cry, a String is really an Array of Char so why does typeof String return “string” and not “object”?
Note: it’s not really. It is, and it isn't. It may be considered like such and it may kind of be stored in a similar manner in some languages - is it in JavaScript? I don’t know … - but a String is a Primitive Type (more on that later) and that means you can’t do what’s in the code to the right.
In all honesty, it’s because it was programmed that way.
There we go Case Solved. It is that way because… what’s that Self? We already said that wasn’t enough? Okay. We can go deeper down.
The command ‘typeof’ and what it really does.
Here we can see what, as discovered on MDN Webdocs page for typeof what can be returned by that method. It is pretty clear that there are a lot of more advanced types of data structures that simply end up defaulting to “object”. Everything else is set as what it is and somewhere in the code that checks what type of data structure it is, when it goes to check an Array it doesn’t meet any of the criteria and defaults to Object.
There. Now we got it.
This explains why.
Wait. No. Not really. Yes, yes Self. I hear you. We will go further.
In reality, it still doesn’t entirely explain why however. Again all it shows us is that some developer made a choice and… I think you may be sensing a theme here. All this explains is that typeof defaults to “object” when it is not one of the above listed Primitive Data Types. What’s that you ask? I am glad you did and so the next twist down into Wonderland leads us to ...
We have to go deeper! Primitive Data Types!
Okay. Next level down. Primitive Data Types. You’ll notice that all of the types that return anything other than “object” are Primitive. Bool, Number, BigInt, String, Symbol, Function are the six types of Primitives in JavaScript.
We ignore Undefined because it is … undefined… and null is a special case… hey! null vs undefined is another great blog to write! Hmm that number on that list keeps getting bigger every time I look away. I wonder if there is a bug in its coding ...
When we want to know what a Primitive is, we look at the MDN on Primitives and discover…
What you may ask now? How does this work when there are clear methods to be called, especially with Primitive’s like String?
Well this is where it gets interesting. In the background, when you call one of these, JavaScript actually calls a constructor to make an Object out of your Primitive, run the method, then garbage collect that object all without ever actually adjusting your Primitive until it returns a new value (if need be) and sets it anew. Why? I am not certain of the exact reasoning, but I am pretty sure it has to do with memory storage and performance. Objects are inherently more complicated to store and act upon, but give much more ability to do things (ie: methods).
However Primitives are basically just a few lines of binary in a memory register. Adding two numbers is very quick and easy for a computer if it has them as binary sequences already. Primitives don’t need more than a few lines of binary ones and zeros to represent them and can be manipulated very quickly for simple operations. Needing to do something complicated however requires more processing power. Requires methods that know what to do with the binary and what to return. All those don’t have to be stored if all we have to do is add 1+1, but when we want to know the length of a string or do some very complex math with a BigInt, JavaScript builds up a new object that has a property inside of “This Int has a value of X” and also has a bunch of methods to do whatever needs to be done with that value. It then returns the proper answer, and destroys that object without you ever knowing it was there.
And this is why ... Wait... is this still just a “Developer Said So” answer? Not really. Well … It is and it isn’t. It has to do with speed, cycles, and a lot more complicated stuff that I only barely understand, but effectively … yeah. A Developer Said So and yet there is a very good reason for it.
Seriously. This is really the reason. A Developer said so and typeof is used for Primitives only to make a call on speed/memory/response cycles at a very deep level.
Well… there is one more level we could talk about.
It is kind of a side jump, but there is one more thing we could discuss in regards to this. It isn’t really the reason per say but it is definitely a byproduct of the way JavaScript was designed that can help shed light on the question at hand. It is higher level than the answer of Primitives vs Objects, but it can very much help.
In JavaScript (and many other modern languages) if you want a simple object that is just a collection of properties, you don’t need to go through the trouble of making a special class and instantiating it and then declaring it and … gah. So much. Just use { } ...
You may see where this is going now.
Array’s are a special case of a basic object. It has special methods applied to it (such as length) and it is ordered in a specific direction, but in reality you could very well initialize a simple object like an array with …
Because what JavaScript is really doing is a key and value hash. When you create an object with a set of properties, those properties are Keys. You can even call them up …
If you code a lot you may recognize this as a Map or a Dictionary from other languages… because it is the same thing. Map/Dictionary/simple Object are all Key/Value hash tables. These types of objects are very good at speedy lookups if you know exactly what the Key is.
Which brings us back to Arrays… where the Indexer [x] is really just a key call!
This brings us full circle. An Array is a speedy way of creating a simple Object with Keys from 0 to n-1 (where n is the number of items in the array) and a value for each. It is much quicker and easier to type out an Array with the know way of initializing it than defining it as an object with numeric keys. It just happens to also be ordered by those keys because of the default fact we as humans understand numbers to have an order (and a Hash Table doesn’t naturally have any specific order). These two special things makes an Array a more advanced and specialized version of an Object.
Is that it then?
So what have we come to. We have discovered that the original devs of JavaScript programmed typeof to only be specific for Primitive Data Types (side note: there is a bug in typeof as well, do you know what it is? It has to do with a special case ...) and return a default of Object for everything that is not a Primitive Data Type (excluding the undefined … Undefined…) The reasoning for this? That is the question we really have come here to answer.
I don’t really know.
Ouch. What a let down. Shh Self. We might have an answer.
I suspect however that it has to do with two things. The way arrays are created as special cases of simple Objects and the fact that JavaScript is a Weak and Dynamic typed language. Weak and Dynamic have a lot more to be said about them than what I’m willing to cover in this blog entry, but suffice it to say that in order for the variables to be mutable and typed at runtime rather than at compile, it is far more efficient to work more with Primitive Types than complex types. (As I said, binary lines in memory registers are much quicker to work with) Certainly any program will use many complex types as well as many Primitive Types, but cutting down time by making Primitives the way they are in JavaScript would save loads of processing cycles over large programs. Restricting typeof to just Primitive Types would make it quicker, defaulting every other data structure type to Object would be true as well, if misleading. It was a design choice, most likely influenced by other design choices, and it’s why it is what it is.