Wednesday, April 18th, 2007
Make the XHR abstraction shorter
In low-level land, the try/catch trick that Prototype uses was king for 'most fun way to get the XHR object'.
Nicolas Faugout has put his hat in the ring with:
-
-
if (!XMLHttpRequest) {
-
function XMLHttpRequest() {
-
return new ActiveXObject('Microsoft.XMLHTTP');
-
}
-
}
-
This feels like the Just Another Perl Hacker hacks in Perl land. Next we will see examples with rot13.












This is so neat! Expect jQuery and MochiKit to be pouncing on this and adding it to their library.
/me looks at date - nope, the 1st has been well gone.
why is this news? the try/catch thing used by most frameworks is because Microsoft.XMLHTTP is not always available (depends on the version of MSXML, or something).
by removing the other versions, you will be restricting your IE audience.
Returning from a constructor is not exactly something you’re supposed to do. It might work in IE, but it won’t work in other browsers.
There is a reason behind using the try catch other than determining version suport:
http://radio.javaranch.com/pascarello/2006/02/07/1139345471027.html
One can forgive Dean Edwards for engaging in mine-is-shorter-than-yours contest as he contributed SO MUCH to the community. But this is just plain ridiculous. It only shows ignorance of the matter.
Some assumptions you might have taken, but are wrong:
On IE6,
!XMLHttpRequestdoesn’t return false here.That’s because IE reads the “function”s on the page before running thru the code. It means, it reads the code, finds all the functions and makes the
functions list for the current scope. When it gets to “function XMLHttpRequest” it defines window["XMLHttpRequest"]=function () { … } (window is the
current scope). Only THEN, after declaring the functions, it goes thru the whole code from line 1.
This is NOT true for Gecko.
function a() {
alert("a");
}
if (false) function a() {
alert("b");
}
a();
In the above code, IE will shout “b”, and FF will shout “a”. That is because what I explained before.
Accessing to a() is available before “function a()” is declared, and IE takes the LAST declaration no matter where it is (assuming it’s in the same scope).
FF declares the if statement as scoped statement.
The following code will explain:
a();
if (true) {
function a() {
alert("a");
}
a();
}
function a() {
alert("b");
}
For FF, the result is “b”, “a”.
For IE6, it’s “b”, “b”.
On IE then, the !XMLHttpRequest returns TRUE because it is declared.
FF ignores the statement because XMLHttpRequest does exist and as mentioned before, doesn’t declares “function XMLHttpRequest” as a functions.
BTW, declaring “f=function ()” is different than “function f()”. The latter is accessible from everywhere in the same scope, unlike the first that accessible only after being declared.
this is what i use
its in the constructor of the core ajax “query” class
//————————————————————–
// constructor
//————————————————————–
this.setGlobals = function(){
if(window.ActiveXObject){isIE = true;}
objREQ = isIE == false ? new XMLHttpRequest() : new ActiveXObject(”Microsoft.XMLHTTP”) ;
my_AJAX = this;
}
A little explaination on my code :
- Elad, I don’t see any incompatibility between what you say and my code. Think about the !XMLHttpRequest beeing here for FF not to redefine XMlHttpRequest.
As for IE, if the function is declared during the reading process you described, that’s great, because it is what we want on IE ;)
- Kae, I agree with you, this code need some diggin
What I wanted to introduce, after the if then else checking browser version stuff and the magic try/catch Prototype solution is a new approach that would consist of defining once for all (at run time) a function named XMLHttpRequest in order not to introduce a new name for instanciating AJAX calls (when not using any framework of course).
Feel free to adapt the idea to your needs (several ActiveXObject uses, etc..)
@Mark: returning an object from a constructor should work in all browsers and is part of the ECMA spec.
@Elad: what you’ve described basically explains why this code works. In IE, the code gets executed and the XMLHttpRequest object gets declared whereas in FF, the code doesn’t run. Of course, this means that IE7’s native object would get overwritten. That’s bad.
I’ve taken the time to write up a solution using conditional comments. Slightly more verbose, though. :)
@Nicolas - That’s bad programming, relying on a logical error, even if it works. Besides, like @Jonathan said, it’ll overwrite IE7’s native object.
If it wasn’t the XMLHttpRequest, or “else” clause would’ve been written, as I mentioned in my code - the two browsers would present different results.
You can put a bit more characters and make it work as it should be and still maintaining the ‘good programming’, by asking for ‘window.XMLHttpRequest’ and assigning to it.
if (!window.XMLHttpRequest) {
window.XMLHttpRequest=function () {
return new ActiveXObject('Microsoft.XMLHTTP');
}
}
I really appreciate staying away from prototype’s ridiculous try/catch technique. It triggers firebug because there is an error for each XHR request. I only visit Ajaxian with IE because of that.
@Elad - I owe you one! I didn’t know IE was reading the script AND assigning functions as it discovers them even neasted in the code. Your version of course solves the IE 7 issue.
@Jonathan, I get this in Firefox:
new function(){return 1} -> [object Object]
5 * (new function(){return 1}) -> NaN
@Kris Zyp,
Um… I’ve never had Firefox/Firebug generate an error using Prototype, nor accessing its Ajax object. Not once.
@Mark - that’s because new operator returns the object itself, which is not 1 but an instance of the function, regardless the ‘return’ statement.
To achieve that kind of syntax, you should override the valueOf method, like this:
function GetFive() {}
GetFive.prototype.valueOf=function () { return 5; }
alert(5*new GetFive());
(very similar like overriding the toString method).
@Kris - Why don’t you just disable Firebug for Ajaxian? I’m not even sure why you’re using it during regular browsing, in my experience it just slows things down. I keep it disabled and only turn it on when I explicitly need it.
Elad, yes, that works. It doesn’t change the example posted here though :)
Interesting. jQuery does the following since the early days:
if ( !window.XMLHttpRequest )XMLHttpRequest = function(){
return new ActiveXObject("Microsoft.XMLHTTP");
};
;) Klaus
This still does detection on every request. Why not branch at load?