I saw this code in Ajax in Action book and there are two things I'm not able to understand (Keep in mind I just started web programming and I'm still trying to understand how JavaScript works).
On line 37 or in function loadXMLDoc, why did the author declared a local variable var loader=this;
and then used it in the call net.ContentLoader.onReadyState.call(loader);
instead of just using net.ContentLoader.onReadyState.call(this);
Why did the author used net.ContentLoader.onReadyState.call(loader);
, instead of this.onReadyState();
/*
url-loading object and a request queue built on top of it
*/
/* namespacing object */
var net=new Object();
net.READY_STATE_UNINITIALIZED=0;
net.READY_STATE_LOADING=1;
net.READY_STATE_LOADED=2;
net.READY_STATE_INTERACTIVE=3;
net.READY_STATE_COMPLETE=4;
/*--- content loader object for cross-browser requests ---*/
net.ContentLoader=function(url,onload,onerror,method,params,contentType){
this.req=null;
this.onload=onload;
this.onerror=(onerror) ? onerror : this.defaultError;
this.loadXMLDoc(url,method,params,contentType);
}
net.ContentLoader.prototype.loadXMLDoc=function(url,method,params,contentType){
if (!method){
method="GET";
}
if (!contentType && method=="POST"){
contentType='application/x-www-form-urlencoded';
}
if (window.XMLHttpRequest){
this.req=new XMLHttpRequest();
} else if (window.ActiveXObject){
this.req=new ActiveXObject("Microsoft.XMLHTTP");
}
if (this.req){
try{
var loader=this;
this.req.onreadystatechange=function(){
net.ContentLoader.onReadyState.call(loader);
}
this.req.open(method,url,true);
if (contentType){
this.req.setRequestHeader('Content-Type', contentType);
}
this.req.send(params);
}catch (err){
this.onerror.call(this);
}
}
}
net.ContentLoader.onReadyState=function(){
var req=this.req;
var ready=req.readyState;
var httpStatus=req.status;
if (ready==net.READY_STATE_COMPLETE){
if (httpStatus==200 || httpStatus==0){
this.onload.call(this);
}else{
this.onerror.call(this);
}
}
}
net.ContentLoader.prototype.defaultError=function(){
alert("error fetching data!"
+"\n\nreadyState:"+this.req.readyState
+"\nstatus: "+this.req.status
+"\nheaders: "+this.req.getAllResponseHeaders());
}
A try/catch
statement in ECMA-/Javascript creates a new Context. Technically, this is similar to an eval
statement and therefore an eval Context
.
The current Scope chain is extended by that newly created "eval Context" and therefore, the Context variable this
, would point to a wrong context when just invoked by this.onReadyState();
.
By calling net.ContentLoader.onReadyState.call(loader);
the author explicitly calls the method onReadyState
with the context of the loaded
object (and that is what this
within the callee is referencing then). A callee is a function (-context...) with was called by a caller (-context).
Long story short, ECMAscripts
.call()
and.apply()
methods allow you to set a specific Context for a function when invoked. This is necessary here, becausetry/catch
creates a new Context and the value ofthis
within the called method would be wrong.
While the above statement is true, it's not responsible here. It's not the Context from try / catch
which is the problem , it's furthermore the Context by the created anonymous function
this.req.onreadystatechange=function(){
net.ContentLoader.onReadyState.call(loader);
}
Using this
within that anonymous method would "again" reference a different Context. That is why the author cached the value from this
in loader
and invokes the method with that cached Context.