samshull
samshull

Reputation: 2328

Why use typeof for identifying a function?

Are there any significant reasons for using

typeof variable === 'function'

versus

!!variable.call

for detecting if a variable is a function?

Other than the obvious one that someone may create an object like:

{ call: 1 }

The problem that I have is that

typeof /regex/ === 'function'

returns true, but

!!/regex/.call

returns false

Upvotes: 9

Views: 5154

Answers (6)

user2335861
user2335861

Reputation: 11

typeof variable === 'function' is better than !!variable.call because if variable is undefined or null, !!variable.call will throw an error.

Upvotes: -1

Wayne
Wayne

Reputation: 60414

jQuery's isFunction avoids the RegExp problem you mention by toString-ing the object and checking the result against a map of known types. From the latest source, here's the map:

// Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
    class2type[ "[object " + name + "]" ] = name.toLowerCase();
});

And here's how it's used:

type: function( obj ) {
    return obj == null ?
        String( obj ) :
        class2type[ toString.call(obj) ] || "object";
},

// See test/unit/core.js for details concerning isFunction.
// Since version 1.3, DOM methods and functions like alert
// aren't supported. They return false on IE (#2968).
isFunction: function( obj ) {
    return jQuery.type(obj) === "function";
},

You can learn a lot reading the jQuery source.

Upvotes: 1

Gumbo
Gumbo

Reputation: 655489

According to the ECMAScript specification, the following should apply for regular expression literals:

A regular expression literal is an input element that is converted to a RegExp object (section 15.10) when it is scanned. The object is created before evaluation of the containing program or function begins.

So typeof /regex/ should yield "object":

typeof /regex/ === "object"

And the constructor of the object created by the regular expression literal should be RegExp:

/regex/.constructor === RegExp

Similar to that, a function definition should yield a Function object:

(function(){}).constructor === Function

But although this returns a Function object, the typeof operator should not yield "object" but "function" instead:

typeof function(){} === "function"

This is due to the distinction whether the object implements the internal [[Call]] property that is special for Function objects.

Note that all this is how Javascript implementations should behave. So all equations are asserted to be true.

Upvotes: 5

Raynos
Raynos

Reputation: 169451

A regular expression is a function

/bar/("bar") === ["bar"]

So typeof /bar/ === "function"

Although only chrome recognises that a regexp literal can be used as a function. Whether this should be so or not is up for grabs. You can treat it just like a function!

Upvotes: 2

user166390
user166390

Reputation:

Check the assumptions in the post (see Gumbo's comment).

typeof /regex/ === 'function'

This returns false in Firefox 3.6.13.

Just for amusement, Firefox 3.6.13:

typeof /regex/                    // "object"
/regex/ instanceof RegExp         // true
/regex/.constructor.name          // RegExp
(function () {}).constructor.name // Function

IE8:

typeof /regex/                    // "object"
/regex/ instanceof RegExp         // true
/regex/.constructor.name          // undefined
(function () {}).constructor.name // undefined

Chrome 9:

typeof /regex/                    // "function"
/regex/ instanceof RegExp         // true
/regex/.constructor.name          // "RegExp"
(function () {}).constructor.name // "Function"

Upvotes: 2

user113716
user113716

Reputation: 322532

The safest way is to check the internal [[Class]] property by setting the object as the thisArg argument of the .call() method when calling Object.prototype.toString.

Object.prototype.toString.call( myVariable ) === '[object Function]';

Of course you could easily make a function out of it:

function checkClass( obj ) {
    return Object.prototype.toString.call( obj ).slice( 8, -1).toLowerCase();
}

checkClass( myVariable ) === 'function';

This is very simple, and there could be some improvements, but you get the idea.

Upvotes: 11

Related Questions