jQuery relies on function decompile, won’t work on Opera mobile

A colleague just noticed that the otherwise excellent jQuery JS library has an "isFunction" method that relies on decompiling functions to figure out if they really, really are functions. This is not a good idea because decompiling functions is an optional feature of the ES-262 spec. It is slow and requires too many resources for certain low-end platforms, so it is not supported by any of the Opera Mobile versions.

Here's the code, complete with a frustrated comment from John Resig himself:

// This may seem like some crazy code, but trust me when I say that this
// is the only cross-browser way to do this. --John
isFunction: function( fn ) {
	return !!fn && typeof fn != "string" && !fn.nodeName && 
		fn.constructor != Array && /function/i.test( fn + "" );
}

John, I feel your pain and that code clearly took quite some effort and testing across various browsers passing in various types of objects – IE collections allowing ()-reference, anyone? It wouldn't surprise me if Opera is one of the culprits that caused you problems here since we try to support some of IE's weirdness and hence "typeof" sometimes returns 'function' when you'd expect it not to.

So I'm sorry to break it to you, but /function/i.test( fn + "" ) won't do what you think on Opera's mobile versions, causing really-hard-to-track-down bugs.

May I suggest adding the below workaround somewhere?

if( (function(){}).toString().match(/^[ecmascript/i) ) Function.prototype.toString = function(){return 'function'};

Should be all you need..

Advertisements

9 thoughts on “jQuery relies on function decompile, won’t work on Opera mobile

  1. Where does Ecma-262 say that Function.prototype.toString() is optional? Because I'm not seeing that.If this has been causing so many problems, why not have it return something like:function () { [function 1234567] }Where the number is unique? That would be fast and satisfy most of the uses I've heard of.

  2. Hm.. I've been told by almighty developers that it was optional. What I see in the spec is a bit ambiguous: section 15.3.4.2 says that Function.prototype.toString output is "implementation dependent" but reading more closely the same paragraph says that it "has the syntax of a FunctionDeclaration". So we're actually wrong to output "[ecmascript code]" but we can output "function dontdecompileme(){}" and the spec is satisfied :-pThe unique string suggestion is useful. I think we should do that for mobile. (off to update the bugs..)

  3. Just thinking "out loud" as it were… could Function.prototype.toString return not just a unique ID, but something able to be eval()d to a working function equivalent to the original function? … I don't know… "function f(){return opera.getFunction(1234567).call(this,arguments);}". I'm not even sure if that sort of thing is possible?

  4. Actually prototype.js also relies on function decompile to parse names of function arguments. Thus some advanced inheritance techniques of this library won't work on mobile devices. Probably this problem may be solved this way: function (arg1, arg2, arg3…) { [function 1234567] }

  5. @hallvord: I think it's not Opera supporting IE's weirdness that's the problem there… I think the main problem is that DOM methods in IE are in fact COM methods and of type "object" not of type "function". I don't know why there isn't a check for "typeof fn == 'function' ", though, so there may be other problems, too.I personally would replace the COM methods in IE in this case, with real ecmascript functions (wrapped around the native "objects"). I'm not sure if this is in line with JQuery policies though…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s