This might be an ultimate optimization weapon. And it might even be considered as a non-hack, not-a-trick, etc.
As an good and simple example, in jQuery there is a function to make and return XHR, depending on which is the current browser host.
1 2 3 4 5 6 7 8 9 |
// Create the request object; // Microsoft failed to properly implement the XMLHttpRequest in IE7, // so we use the ActiveXObject when it is available // This function can be overriden by calling jQuery.ajaxSetup xhr: function() { return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest(); }, |
Good logic, simple implementation. Yes, but… This function is called a lot and each time , precious microseconds are wasted to find out which version is to be used, made and returned.
And. Each time the result is the same, because this keeps on being called in the same browser session. What first springs out as an obvious optimization is this :
1 2 3 4 5 6 7 8 |
// Create the request object. // Decide upon first pass which version is to be used. // This function can be overriden by calling jQuery.ajaxSetup xhr: window.ActiveXObject ? // IE browsers function() { return new ActiveXObject("Microsoft.XMLHTTP") ; } : // non IE browsers function() { return new XMLHttpRequest(); } |
The condition above is evaluated only when the whole object in which ‘xhr’ resides gets evaluated and made. In jQuery case, this is hopefully only once, when jQuery is made in its closure.
But there is one more “ultimate” version I can think of. Even more optimized version we can write. This time we will use Microsoft JavaScript conditional compilation feature. It is part of javascript
- only
when IE is the host browser.
1 2 3 4 5 6 7 8 9 10 |
ajaxSettings = { xhr: function() { return new XMLHttpRequest(); } //@cc_on , xhr: function() { return new ActiveXObject("Microsoft.XMLHTTP"); } } alert(ajaxSettings.xhr) /* in IE shows : function() { return new ActiveXObject("Microsoft.XMLHTTP"); } in FF, Chrome, etc. shows: function() { return new new XMLHttpRequest(); } */ |
What is happening here? First we defined xhr() version for all the browsers but IE
1 |
xhr: function() { return new XMLHttpRequest(); } |
Then comes the comment bellow it , which contains Microsoft JavaScript syntax that switches
on, the conditional compilation : “//@cc_on”. This also works inside another comment syntax and if in IE, uitside of comments, anywhere in javascript code.
So, if this comment is inside IE javascript, it will be made part of the source code, effectively overriding the previous version of xhr(). In IE the code will now look like this:
1 2 |
xhr: function() { return new XMLHttpRequest(); } xhr: function() { return new ActiveXObject("Microsoft.XMLHTTP"); } |
The second version of xhr() will overwrite (aka override) the first version. The first version is erased, the second now exist, and is being used when called.
There you have it: an example of ultimate optimization. I very much doubt I have invented this. But neverhelles I will suggest it as an very effective mehanism for cross browser libraries.
Update 2009 Nov 25
There is one very elegant way to use condtional “compilation” to distinguish between IE and non-IE code:
1 2 3 4 5 |
if (/*@cc_on!@*/false ) { // IE code } else { // non IE code } |
Simple and effective. In IE the above is parsed into :
1 2 3 4 5 6 |
// what IE sees if (! false ) { // IE code always } else { // non IE code, never } |
While everywhere else code is parsed as :
1 2 3 4 5 6 |
// what everybody else sees if ( false ) { // never reached : IE code } else { // non IE code: always reached } |
Arguably this is more code and also, this is if/else which is always executed but still in some situations this is very valid, and maybe not a “trick”.
Alternatively, you are wellcome to use the ultimate in JS conditional “compilation” :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/*@cc_on @if (@_jscript_version > 5.7) document.write("You are using IE8+"); @elif (@_jscript_version == 5.7 ) if ( window.XMLHttpRequest ) document.write("You are using IE7"); else document.write("You are using IE6"); @elif (@_jscript_version == 5.6 ) document.write("You are using IE6"); @elif (@_jscript_version == 5.5) document.write("You are using IE5.5"); @else @*/ document.write("This text is seen by: Firefox, IE 4.x, Chrome, Opera, Safari, etc)"); /*@end @*/ |
Enjoy ;)
6 thoughts on “JavaScript Legacy — Optimization through Conditional Compilation”
You see…
Using JScript’s CC to declare XHR wrapper in IE is essentially a feature inference; You make an assumption that if client supports conditional compilation it also supports
ActiveXObject
. Any inference is usually less reliable than direct feature detection / testing. Your assumption might hold true in current version of IE, but can fall apart in a future one (or another client, implementing JScript’s CC).The bottom line is that it’s rarely a good idea to use inference when testing feature itself is trivial, such as in this case. I don’t see why plain and simple –
if (typeof ActiveXObject !== 'undefined')
– is not enough here.P.S. Your CC -based snippet does double assignment in object literal; something that is considered a bad practice and will throw error in ES5-strict, if I’m not mistaken.
—
kangax
I think, you are saying I (just) think I have “found” a feature, which actually is theoretically flawed because it is based on false premises aka assumptions ? You, see my dear “oponent”, “feature inference” is actually very usefull way of thinking, for deducing (infering) optimizations. For example :
(Definition:”Inference is the act or process of deriving a logical consequence conclusion from premises.”)
Theoretically yes, my premises might be false, but what that means in practice? Some future MS JavaScript (aka JScript) *might* not have conditional compilation any more. Practically, the chancess of this happening in a foreseable future are : zero. Same as chances of some other JavaScript vendor implementing the same feature. Or same as chances of FireFox overtaking IE. Zero.
So I really can deduce, that this is a good practical feature one should use to simply “comment out” or “comment in” source code, depending on the platform, constants, etc. After all, this is a feature used in most compiled languages since the “begining” of computing.
How much is above mechanism ubiquotus across languages, is well known. Even JScript does it.
My example (in the post) is made deliberately very simple. Why are you commenting on its details, I do not understand? This is good but trivial code, just to explain the concept. That code can be made much more robust (if necessary) by using all the features like @if, @else, inbuilt variables like @_win32 , or even user defined variables like @set @IE = true . Etc … There is plenty of non-trivial examples on the net.
A “plain and simple”
if (typeof ActiveXObject !== ‘undefined’)
is lastly not good enough, by your own conclusion? You say it may dissapear from future JScript? Well I will boldly take that risk and I will use it , for several decades more, until it might be dropped from JScrpt. In any case that is an irrelevant point for this posting. I could have used any other generic example.“double assignment in object literal” ? Google (at least for me) retruns 0 matches on that phrase. Is this a “bad practice”. Will it throw an error if ECMA5 “use strict” is present? I might be rushing, but in ECMA5 spec, I could not find any reference to this?
Thank You for Your comment : Dusan
Of course the “ultimate” optimization would be this:
Where IE specific version will be overwrites of certain “stuff” just if necessary . Not full versions of Jquery for each IE. Clear ?
Why is no one doing it this way ? Hm… There must be a reason ?
Thanks for article. Everytime like to read you.
Zoran
I understand now (the interesting point of) your solution which has the advantage
of being executed once even inside functions that are called more than once.
I don’t understand why it isn’t more used in libraries.
Ludovic
________________________________________
Date d’envoi : jeudi 20 août 2009 12:22
À : PATEY Ludovic
FFWD to to day (2015 Q1)
IE will be replaced with “Project Spartan” , new MSFT developed browser. Also conditional compilation does not work in IE11 already. Therefore we can not use conditional compilation.
One sure way approach is to look into the “User Agent” .. and in the case of IE look for a “Trident” word/ Therefore:
[code]
var IE = /\([^)]*Trident[^)]*rv:([0-9.]+)/.exec(navigator.userAgent);
[/code]
And
[code]
var xhr = IE
? // IE browsers
function() { return new ActiveXObject("Microsoft.XMLHTTP") ; }
: // non IE browsers, "Spartan" including
function() { return new XMLHttpRequest(); }
[/code]
Enjoy :)