Monday, 14. January 2008, 17:53:41
decompile, coding, compatibility, jQuery
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..
Saturday, 7. July 2007, 11:08:40
prototype, compatibility, coding, decompile
Prototype 1.5.0 completely breaks CNN on certain mobile versions of Opera. All the text disappears.
We've dug up the source and it appears to be code from the common prototype.js that causes problems, CNN's
prototype.js is here:
Element.extend.cache = {
findOrStore: function(value) {
return this[value] = this[value] || function() {
return value.apply(null, [this].concat($A(arguments)));
}
}
};Problem arises when the "value" argument is a function object. Then this[value] = this[value] requires decompilation, in order to create a string for the property name.
This is a bad idea because
- Decompilation support is an optional feature. We disable it on many limited platforms for performance
- Decompiling a function is in any case not a reliable way to create a unique hash key since functions can have exactly the same source code but live in different scopes or different closures, so this code as-is will create obscure bugs that are very hard to track down.
I hope the excellent Prototype devs can find a better approach. Unfortunately this code is still there in
the latest version.
(Due to impatient family members I can't bug it in their
tracker right now, post a link to the bug in comments if you do.)
Wednesday, 28. March 2007, 18:23:46
decompile, coding, addEvent, standards
...
A while ago, Peter-Paul Koch and some other JavaScript-interested bloggers held a competition for improving the "addEvent" function. The
winner was John Resig's
flexible JavaScript events function.
It creates a new property of the object you add an event listener to as follows:
obj['e'+type+fn] = fn;
The main purpose of that is to make the "this" keyword work inside event listeners. It is a clever hack, though looks a bit ugly - to ensure that the object property has a unique name, it concatenates "type" and "fn", meaning the browser
has to decompile the function and use its entire source as part of the property name. Decompiling a function object is slow, the property name becomes nothing short of ugly. Perhaps a few lines of code to make a prettier unique property name would be worth it?
Besides the ugliness, here comes the real problem:
the code assumes that all implementations can decompile functions. Opera can on desktop, but to save footprint this feature is disabled on mobile phones. Function.prototype.toString simply returns "[ECMAScript code]". Hence, each call to Resig's addEvent will overwrite the previous function object for that type, and the last function added will be called as many times as addEvent itself has been called. I'm afraid the clever hack has turned rather destructive - bad enough to break cross-platform compatibility for any app.
As written, the function makes the assumption that all browsers which have attachEvent support has IE's bugs. The simple fix would be to follow this rule: use object detection and
try the standards-compliant path first. First looking for the standards-compliant (thus probably the best defined) function might be a good general rule of thumb for compatibility. Cross-browser support for a method that is implemented by trying to figure out what another browser is doing is likely worse than support for a method specified in detail by the W3C, so by choosing the W3C stuff if it exists should get you better compatibility.