Testing for Object Types in JavaScript

The other day, I was writing a JavaScript function and came across an interesting

problem: how to get the type of an object (Date, String, RegExp, Array, Number, etc.)

programmatically. The situation is simple. Let’s say you have a function that can take

either an Array or a String as an argument. If the argument is an Array, you can (of course)

invoke Array methods on it, but if the argument is a String, you’ll get runtime errors if

you try to invoke Array methods. So, how do you tell whether the function was passed an

Array versus a String?

Languages like VBScript have an isArray() method, but JavaScript doesn’t.

JavaScript has a typeof operator. But typeof testing can be tricky. For

example:


var abc = 'ABC';var def = new String('DEF');var t1 = typeof abc; // t1 = =

'string'var t2 = typeof def; // t2 == 'object'

Type vs Object

You thought abc and def were both strings? Think again. Anything created by

a constructor is (by definition) an object and will give a result of ‘object’ when

tested by typeof. A string literal is a direct representation of the low-level

‘string’ data primitive in JavaScript. You can invoke String-object methods on variables of

the ‘string’ type, but when you do that, what’s happening behind the scenes is that the

runtime interpreter is actually promoting the string literal to a String object

(temporarily).

The long and short of it is that if you use the ‘typeof’ test to test whether a string is

a String, you’ll get into trouble a good deal of the time. A String object is an object, not

a string.

Confused yet? It gets worse.

(In case you haven’t noticed, I capitalize String when I’m referring to the object form.

Lowercase string refers to the data type.)

At this point you’re probably thinking, okay… to distinguish a String from an Array,

you can just invoke a String method and see if it works. If it works, the input was a

String.

The problem is, if you invoke a non-existent method on an object, you get a runtime

error. Try invoking charAt() on an Array object and you’ll see what I mean. Not a

good thing to do.

What about properties? Surely Array objects and String objects have different properties.

Find one that is unique to Strings, say, and test its definedness with typeof.

Right?

Wrong. Strings and Arrays have just one property, called length.

Stumped again.

The Solution

I looked online for other answers to the isArray() problem in JavaScript, figuring

why should I reinvent the wheel if somebody else has sweated through it already. But I found

only non-answers and incorrect answers.

Time to do a little hacking.

What I came up with relies on the fact that every object in JavaScript has a constructor

function, which you can access explicitly by means of the constructor property. As an

experiment, I suggest you try:

app.alert( (new Array).constructor );

Attach that code to a button in a PDF form and run it. You’ll get a dialog that says

function Array() { [native code] }

The trick, then (are you thinking what I’m thinking?), perhaps, is to cast the object’s

constructor to a String and test the result for the presence of the substring ‘Array’ (if

we’re testing for arrayness). In other words:


// Return a boolean value telling whether // the first argument is an Array object.

function isArray() {if (typeof arguments[0] == 'object') {  var

criterion =

    arguments[0].constructor.toString().match(/array/i); 
 return (criterion != null);  }return false;}

You can pass my version of isArray() any number of arguments, including none at

all. If arguments were passed, we only care about the first (actually the zeroth) one. We

test that one, which is to say arguments[0], to see if it is of type ‘object’. If it

is not, we return false. If the first passed-in argument is indeed an object, we need to

check it to see if it harbors an Array. This is where the rubber meets the road. First, we

grab the object’s constructor. (Remember, every object has one.) We cast it to a string with

toString(): an essential step. And finally, we look inside the resulting String to

see if there is an occurrence of the word ‘array’ (in upper or lower case). The way the

match() method works is that any successful match against the regular expression

supplied as an argument will return an array, the zeroth element of which is the match text.

On failure (no match), null is returned. Therefore, we must check the end result of this

mess for non-nullness. And we return the corresponding boolean value.

isString()

A small change to the above code has to be made before it will work as a check of

stringness. Can you see the problem? You can’t just substitute a new regular expression at

the end (/string/i in place of /array/i), because if you do, the code will

return false if you do isString(‘abc’).

Here is the properly modified code:


// Return a boolean value telling whether // the first argument is a string.

function isString() {if (typeof arguments[0] == 'string') return true;if

(typeof arguments[0] == 'object') {  var criterion =

    arguments[0].constructor.toString().match(/string/i); 
 return (criterion != null);  }return false;}

The first line of the function checks to see if the argument is a string primitive

(string literal). This is key, because not every String is a string. (See discussion further

above.) Some strings are inside object wrappers!

Next time, maybe we’ll talk about why I got wrapped up in all this arrayness and

stringness stuff in the first place. It’s all for a good cause. Trust me!

You May Also Like

About the Author: Kas Thomas

Leave a Reply