In a complex library I've been working on, I got pretty tired of all of the individual type functions and operators to determine whether I was working with an object or piece of data of a given type. This was especially true when I was checking for inherited prototypes between classes. In an effort to alleviate, I made this generalized is() function.
( function( root ) {
var is = function( v, t ) {
if ( typeof v !== 'undefined' ) {
if ( typeof t === 'string' )
return typeof v === t;
else if ( typeof t === 'function' ) {
if ( typeof v === 'object' )
return v instanceof t;
else if ( typeof v === 'function' )
return t.prototype.isPrototypeOf( v.prototype );
else return false;
}
}
return false;
};
root['is'] = is;
}( this ) );
Syntax
is(variable, type)
where type is any return fromtypeof
- returns true if
variable
typeof ===type
- returns true if
is(variable, class)
where variable is an object and class is a constructor- returns true if
variable
is an instanceofclass
- returns true if
is(class1, class2)
where class is a constructor and class is a constructor- returns true if
class1
's prototype is a descendant ofclass2
's prototype.
- returns true if
Usage
I use this function whenever the type of a value is imperative. One common example is when I need different behaviors depending on the type. As an example:
// typeof myVar === 'number'
if(is(myVar,'number')) {
doSomething();
}
// myVar instanceof Array
elseif (is(myVar,Array)) {
doSomethingWithArray();
}
// typeof myVar === 'object'
elseif (is(myVar,'object')) {
doSomethingWithObject();
}
Reasons for this function
The goal is to reduce the amount of typeof
, instanceof
and isPrototypeOf
statements, as well as the need for individualized type checking methods. This certainly seems to accomplish much of that goal. A nice little beneficial side effect is that it reduces the code of using libraries significantly. I've been utilizing it in some newer libraries (at a minified overhead of 150 bytes) and it has saved me much more than that, as well as an uncountable number of keystrokes.
Reason for this Question
While I haven't noticed a performance hit, I would like a review with more quantifiable statistics. Additionally, should I have special handling for falsey values? Are there other gotchas that I'm not accounting for?
Here is the minified code:
(function(c){c['is']=function(a,b){if("undefined"!==typeof a){if("string"===typeof b)return typeof a===b;if("function"===typeof b){if("object"===typeof a)return a instanceof b;if("function"===typeof a)return b.prototype.isPrototypeOf(a.prototype)}}return !1}})(this);
Update: Performance
After performing some of the suggested changes, since no one addressed performance, I took some time and learned me some jsPerf. The switch/case variant, performed much more slowly than the if/else variant. Further, I made another variant using the ternary (?) operator which performed much better. Here is the test.
Since type-checking is generally quick and fairly uncommon compared to many other operations, I'm certainly willing to take the hit.
Update: Revised Code (using comments and answer)
( function( root ) {
// To force minifiers to ignore function name
root['is'] = function(value, type) {
if ('undefined' !== typeof value) {
switch (typeof type) {
case 'string':
return typeof value === type;
case 'function':
switch (typeof value) {
case 'object':
return value instanceof type;
case 'function':
// Account for default behavior
return type === Function
? true
: type.prototype.isPrototypeOf(value.prototype);
}
}
}
// Account for passed undefined values
return ('undefined' === t)
? true
: false;
};
}( this ) );
is(v, t)
problem because duck-typing is the norm. \$\endgroup\$is(v, Array)
to returntrue
whenv
is an array, butis(v, Function)
to returnfalse
whenv
is a function andis(v, Number)
to returnfalse
whenv
is a primitive number. (is(v, 'function')
andis(v, 'number')
work correctly, though.) Also,is(v, 'undefined')
can never returntrue
. \$\endgroup\$