typeof, isFunction vs. isCallable, now and in ECMAScript 5

| 9 Comments

Function objects in JavaScript are callable: you can invoke them. Other, non-function objects are allowed to be callable, however. Host objects in IE (things like Window.alert()) are callable, but are not native function objects. (Other browsers implement DOM methods as true native function objects). Also, a number of browsers have followed Firefox's lead in making RegExp objects callable even though they are not functions. Although you can invoke any callable object like a function, the difference is that callable objects don't have function methods call() and apply() (and bind() in ECMAScript 5).

Today, the typeof operator returns "function" for true function objects, and returns "object" for IE's callable host objects. Most browsers return "object" for callable RegExps, but Safari returns "function", and Google Chrome is likely to follow Safari's lead on this. If you want to be sure that something is a true function (and not a regexp) you can use something like this:

function isFunction(x) { 
    return Object.prototype.toString.call(x) === "[object Function]";
}

There is not today a reliable way to write an isCallable() function, however.

Things change in the ECMAScript 5 specification. The typeof operator is required to return "function" for any native or host object that is callable. When IE implements the spec, we can expect "typeof window.alert" to evaluate to "function". The problem is that browsers like Firefox and Opera that have callable regexps are unlikely to implement the spec: the typeof operator on a regular expression will continue to return "object" for those browsers. The committee writing the specification was aware of this problem, but ran out of time to fix it.

So today typeof x === "function" is close to an isFunction() test, but it fails for regular expressions in some browsers. In ECMAScript 5, typeof will be close to an isCallable() test, but it will fail for regular expressions in some other browsers.

Fortunately, the isFunction() test above should continue to work in ECMAScript 5, and
there is a way to write a reliable isCallable() function in ECMAScript 5. It relies on the fact that the Array.prototype.forEach() method checks its argument for callability even when invoked on an empty array. (So this isCallable() function assumes that browser vendors implement the Array.forEach() method as specified.) Here it is:

Object.isCallable = function(o) {
    // Array.prototype.forEach throws TypeError for non-callable arguments
    try {
        [].forEach(o);  // o will never be invoked, but it will be tested for callabilty
        return true;
    } catch (x) {
        if (x instanceof TypeError) return false;
        else throw x;
    }
};

9 Comments

Hi David.
Very intersting, but in IE isFunction does not work :(

//IE isCallable
Object.isCallable = function(o)
{
if ((o instanceof Object && !(o.call instanceof Function)) || o.nodeType == 1)
{
return false;
}
var root = document.documentElement;
try {
root.attachEvent('onload', o);
return true;
}catch(e)
{
return false;
}
}

Asen,

My isFunction() call does work in IE (IE8, at least). The issue is that in IE, none of the callable function-like things that are part of the DOM are function. They're callable objects. isFunction() detects native functions.

There is still the problem of not being able to tell what host objects are callable.

Interesting use of attachEvent for callable detection. You'd want to call detach event unless you were confident this was being used after onload had fired, of course...

When I said "no reliable way" to write isCallable(), I meant no way guaranteed by any specification.

Hi again David. Sorry for my flood.

"When I said "no reliable way" to write isCallable(), I meant no way guaranteed by any specification."
Yes understand you, but i try to do it, "isCallable" for IE. In IE, when you try to attach new property on the native method or host function, throws error. And my code for detection:

//IE isCallable
Object.isCallable = function(c)
{
if (!c) return false;
if (c instanceof Function)
{
return true;
}
try {
c.__call = c.__call;
return false;
}catch(e)
{
return true;
}
}

Hi again David. Sorry for my flood.

"When I said "no reliable way" to write isCallable(), I meant no way guaranteed by any specification."
Yes understand you, but i try to do it, "isCallable" for IE. In IE, when you try to attach new property on the native method or host function, throws error. And my code for detection:

//IE isCallable
Object.isCallable = function(c)
{
if (!c) return false;
if (c instanceof Function)
{
return true;
}
try {
c.__call = c.__call;
return false;
}catch(e)
{
return true;
}
}

The first isFunction() is faster and as robust compared to the second:

function isFunction(x){ return !!x && x.constructor == Function; }

function isFunction(x) {
return Object.prototype.toString.call(x) === "[object Function]";
}

David, can you explain your implementation of the isFunction() function since there are other faster and shorter solutions?
Regards,
Les

Anonymous:

Testing the constructor property (or using instanceof) doesn't work if you're working with objects from more than one window or frame. The Function constructor in one window is !== the Function constructor from another window.

Les:

typeof does not always distinguish between functions and regexps. As noted above, instanceof and constructor do not work if you've got objects from multiple windows or frames. My isFunction() test is a lot like an isArray() test that uses Object.prototype.toString to reliably detect arrays.

David

Simply test if .apply or .call exists on the "callable" object, just like you said yourself in the first paragraph:

if(obj.call) ...


is all you need

wizzszz,

That test would treat Function.prototype as a function. I'm talking about tests that are guaranteed to work.

Leave a comment

Books

Comprehensive coverage of Ruby 1.8 and 1.9

"The New Most Important Ruby Book"
Peter Cooper,
rubyinside.com

Completely updated for Ajax and Web 2.0

"A must-have reference"
Brendan Eich,
creator of JavaScript

The classic Java quick-reference

Advertising

Pages

Hosted By

Powered by Movable Type 4.21-en