Polymorphic functions is a great concept of programming. That’s a function which treats parameters differently, depending on their type.
To support polimorphism, we need a way to get the type of a variable. There is a small JS zoo here, let’s check it out.
As we know, there are several primitive types in JavaScript:
- null, undefined
- Special values.
- number
- All numbers like
0
and3.14
. Also pseudo-numerical valuesNaN
andInfinity
- boolean
True
andfalse
values.- string
- Strings, like “Meow” and “”.
All other values are objects. For example, functions and arrays are objects.
Typeof
operator
The typeof
operator takes a value and return it’s type. There are two possible syntaxes:
- Operator:
typeof x
- Function-like:
typeof(x)
Both syntaxes work same, but the first one is shorter.
typeof undefined // "undefined" typeof 0 // "number" typeof true // "boolean" typeof "foo" // "string" typeof {} // "object" *!* typeof null // "object" typeof function(){} // "function" typeof NaN // "number" */!*
You see, the three last lines are red. That’s for a reason.
- First, the
typeof null == "object"
is a official mistake in the language, carefully kept from 90x for compatibility.We can check it:
var x = null x.prop = 1 // error, can't assign a property to primitive
- Functions are still objects despite of their special treatment.
- The
typeof NaN == 'number'
is funny, becauseNaN
is an acronym for ‘Not-A-Number’
Only primitive values
The typeof
works sufficiently well with primitive values (except null
). But it says nothing about object types.
The typeof
can’t distinguish between objects.
For example, Object
, Array
and Date
objects are same.
alert( typeof {} ) // 'object' alert( typeof [] ) // 'object' alert( typeof new Date ) // 'object'
We can also see the difference between a primitive string and new String
:
alert( typeof "lalala" ) // string alert( typeof new String("lalala") ) // object
A good use of typeof
Let’s write a polymorphic function.
function f(x) { if (typeof x == 'function') { ... // in case when x is a function } else { ... // in other cases } }
That was a fair use.
A bad use of typeof
Now the antipattern. In old code you can find a test for variable existence with typeof
:
// check if global variable jQuery exists if (typeof(jQuery) !== 'undefined') ...
The typeof
is used here, because a direct usage of an undefined variable would lead to an error:
// error if jQuery is not defined if (jQuery) { .. }
Don’t use typeof
this way.
A global variable can be accessed as a property of built-in window
object. Let’s check it:
if (window.jQuery !== undefined) { ... }
There will be no error, because no one asks for (probably undefined) jQuery
. We are just asking the object property.
In most cases, like here, we know that jQuery
can’t be falsy if it exists, so the check shortens to:
if (window.jQuery) { ... }
The typeof
shouldn’t be used to check for variable existance.
[[Class]]
to differ between native objects
The main problem with typeof
is that it doesn’t tell much about the object. The function
is an exception.
alert( typeof {key:'val'} ) // Object is object alert( typeof [1,2] ) // Array is object alert( typeof new Date ) // Date object
Fortunately, there is a hidden [[Class]]
property in all JavaScript native objects. It equals “Array” for arrays, “Date” for dates etc.
This property is not accessible directly, but toString
, borrowed from native Object
returns it with a small wrapping:
var toClass = {}.toString // (1) alert( toClass.call( [1,2] ) ) // [object Array] alert( toClass.call( new Date ) ) // [object Date]
Here’s what we do:
- Copy a reference to
toString
for objects intotoClass
variable. - Call it, but provide the object which we are going to check as
this
. So, objecttoString
becomes applied to arrays, dates etc. We have to do it, because their owntoString
behaves differently. For arrays it returns element list, for dates it returns the string representation.
The method also works on primitives:
alert( toClass.call(123) ) // [object Number] alert( toClass.call("primitive")) ) // [object String
… With exception of null
and undefined
, because calling call
with null
or undefined
passes window
as this
.
The [[Class]]
is “Object” for custom objects:
function Animal(name) { this.name = name } var animal = new Animal("Goofy") var class = {}.toString.apply( animal ) alert(class) // [object Object]
So, it is useful only for native objects.
Checking type for custom objects
For objects, created by constructor functions, we usually use instanceof
operator.
It’s syntax is obj instanceof Func
:
function Animal(name) { this.name = name } var animal = new Animal("Goofy") alert( animal instanceof Animal ) // true
We talk more about it in the article The "instanceof" operator.
As of now, for native objects [[Class]]
approach is better and is used in most frameworks.
We could detect an array by using the instanceOf
operator:
var arr = [1,2,3] alert(arr instanceof Array) // true
Although, it will not work if arr
was created in another window or iframe and then passed into current window. That’s because each window has it’s own object hierarchy.
To workaround that, most frameworks use [[Class]]
for native objects.
Summary
typeof
- Good for primitives and functions, it lies about
null
. [[Class]]
- Exposed through
{}.toString
. Good for built-in object and primitives, exceptsnull
andundefined
. instanceof
- Works for custom objects. Can be used for native objects too, but lies if they come from another frame/window.