13

This is probably a stupid question, so please stick with me.

Why do I see so many examples testing whether an object is a Function by comparing its toString() to "[object Function]"?

For example:

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

Can't we use instanceof Function or obj.constructor === Function? Are those not cross-browser compatible?

This seems inefficient, but is it? Why?

Eric Wendelin
  • 43,147
  • 9
  • 68
  • 92
  • possible duplicate of [jQuery's isFunction and InternetExplorer](http://stackoverflow.com/questions/3514336/jquerys-isfunction-and-internetexplorer) – meder omuraliev Dec 03 '10 at 16:04

2 Answers2

7

Short answer is because typeof /foo/ is a function in Webkit browsers. CMS has the long drawn explanation @ jQuery's isFunction and InternetExplorer

And instanceOf isn't reliable because as Zuriy points out:

The problems arise when it comes to scripting in multi-frame DOM environments. In a nutshell, Array objects created within one iframe do not share [[Prototype]]’s with arrays created within another iframe. Their constructors are different objects and so both instanceof and constructor checks fail:

Great article @ http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/ by Zuriy on the subject.

Example taken from the article:

var iframe = document.createElement('iframe'); 
document.body.appendChild(iframe); 
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]  

// Boom! 
arr instanceof Array; // false  

// Boom! 
arr.constructor === Array; // false
Community
  • 1
  • 1
meder omuraliev
  • 183,342
  • 71
  • 393
  • 434
  • when/why isn't instanceOf not reliable? – Matt Dec 03 '10 at 16:08
  • I can see why "instanceOf" is bad, but what about using "obj.constructor". Can we get an example? – Eric Wendelin Dec 03 '10 at 16:10
  • Ok, so in multi-frame environments, constructor fails. Gotcha. Thanks!! – Eric Wendelin Dec 03 '10 at 16:14
  • 1
    Unfortunately, Kangax's solution doesn't solve the multi-frame issue for Internet Explorer 7 and lower, because they return `[object Object]` for an object or function that was created in a different context. I mentioned this in [this answer](http://stackoverflow.com/questions/1058427/how-to-detect-if-a-variable-is-an-array/4029057#4029057) regarding testing to see if an object is an array, a similar solution could be applied to functions. – Andy E Dec 03 '10 at 16:16
  • 1
    @Andy: Funny, I've written about that very thing twice on SO today already. – Tim Down Dec 03 '10 at 16:17
  • @Tim: the downside is that I tried and couldn't come up with a viable fix that didn't use browser detection. The upside is that it's definitely fixed in IE 9. – Andy E Dec 03 '10 at 16:22
1

They are not testing its toString method, they are calling Object's prototype's toString method on obj to cast it to a string. A function casts as '[object Function]' to a string.

instanceof is not reliable and neither is the constructor way. The constructor requires a check to see if obj is null or not before attempting to access its constructor property - I'm also guessing it's a tad slower than the toString method.

Horia Dragomir
  • 2,858
  • 24
  • 21
  • I can see why "instanceof" isn't reliable (example function Array(){}), but where would "constructor" fail? – Eric Wendelin Dec 03 '10 at 16:08
  • 1
    @Eric for exactly the same reason as "instanceof" being a problem: the "constructor" reference would be different for objects from two different frames, even if they were constructed by the "same" function, because each *instance* of the constructor function (in each separate window) is distinct. – Pointy Dec 03 '10 at 16:40