8

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

samshull
  • 2,328
  • 1
  • 14
  • 13

6 Answers6

11

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.

user113716
  • 318,772
  • 63
  • 451
  • 440
  • 1
    This works unless you redefine the internal `Object.prototype.toString` function. ;) – Gumbo Feb 20 '11 at 01:05
  • Could you take a look at this question: http://stackoverflow.com/questions/5055061/null-undefined-are-replaced-with-the-global-object-in-all-browsers-except-ie9 ? – Šime Vidas Feb 20 '11 at 03:33
  • 1
    @Gumbo: I missed your comment earlier. Good point. Can't we have just one bit of certainty in javascript?! – user113716 Feb 20 '11 at 04:56
  • This solution does not work for IE8 and below. Since those browser will think built-in function as object. Please check: http://stackoverflow.com/questions/8100444/why-some-built-in-functions-in-ie8-are-not-instances-of-function – Ricky Jiao Sep 25 '14 at 03:05
  • This is a horrible solution for not being able to work regardless of modifying or redefining built-in properties. `Object.prototype.toString = function () {}` breaks this. – Melab Aug 10 '21 at 11:46
5

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.

Gumbo
  • 643,351
  • 109
  • 780
  • 844
2

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"
user113716
  • 318,772
  • 63
  • 451
  • 440
2

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!

Raynos
  • 166,823
  • 56
  • 351
  • 396
1

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.

Wayne
  • 59,728
  • 15
  • 131
  • 126
  • very smelly and stringly typed, -1 for suggesting to learn from it (BTW, monkey style array initializer 6-8 times slower than regular unlazy literal) – Free Consulting Feb 20 '11 at 01:49
  • 1
    @Worm - appreciate the explanation for the -1. I disagree that the performance issue here is worth worrying about and think there's still something to learn here, even if you decide not to use it. But thanks for the time to explain. – Wayne Feb 20 '11 at 04:00
  • You'll note that the accepted and top-voted answer uses a less general version of the jQuery approach I described. – Wayne Feb 20 '11 at 04:01
  • Well, sure, top answer exercises "string typization" too. Anyway, pattern is same as `boolean.toString().length == 4` – Free Consulting Feb 20 '11 at 14:40
  • Complaining that this is "stringly typed" is a little unfair. JavaScript's `typeof` is notoriously broken. It's not as though we're ignoring clearly better alternatives. Google provides the following definition for "stringly typed": "A riff on strongly-typed. Used to describe an implementation that needlessly relies on strings when programmer- and refactor-friendly options are available." 1) This isn't needless. 2) Programmer- and refactor-friendly options are not available. – Wayne Feb 20 '11 at 15:56
  • IMHO code like this is as bad as `eval("var "+name+"="+value";")` but sadly jquery is very popular. – Vitim.us May 06 '13 at 01:14
  • -1. jQuery is a fabulous way to degrade the performance of any basic operation by one or two orders of magnitude. This is only one example. In all but a handful of cases in the entire history of the universe, `typeof f==="function"` does exactly what you want and does it many times faster than alternatives. Please quit whining. –  Jul 12 '13 at 17:35
  • 1
    In all but a handful of cases in the entire history of the universe the performance issue debated here doesn't matter – Wayne Jul 12 '13 at 17:54
  • I like this answer just because I noticed that $.type(/test/) === "regexp" is available if you are using jQuery. And no, this isn't a vote for jQuery. I upvoted the top answer too. :) – Derek Oct 22 '13 at 20:59
-1

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

Robert
  • 5,278
  • 43
  • 65
  • 115