I've started writing my own library, and I originally attempt to make
it appear similar to Prototype, in the hopes that I could easily get
the other developers off Prototype and eventually improve our site.
The following is the "Ajax" section of the code, and I would greatly
appreciate (and request) critiques of the code from the group.
Ajax = {
transport : (function(){
if(window.XMLHttpRequest){
return new XMLHttpRequest();
}else if(window.ActiveXObject){
return new ActiveXObject('Microsoft.XMLHTTP');
}else{
return false;
}
})(),
encodeURIParams : function(params){
var url='',
noCACHE = Math.random()*10e17,
key, i;
for(key in params){
if(params.hasOwnProperty(key) && key != 'noCACHE'){
//in the case of a select multiple
if(typeof params[key] == 'object' &&
params[key] instanceof Array){
for(i = params[key].length; i--;){
url += encodeURIComponent(key) + '=' +
encodeURIComponent(params[key][i])+'&';
}
}else{
url += encodeURIComponent(key) + '=' +
encodeURIComponent(params[key])+'&';
}
}
}
if(!params.hasOwnProperty('noCACHE')){
url += 'noCACHE='+noCACHE;
}else{
url = url.substring(0, url.length-1);
}
return url;
},
Request : function(url, params, onsuccess, method){
var xhr = Ajax.transport,
res = '',
post = null;
xhr.onreadystatechange = function(){
if(onsuccess && xhr.readyState==4){
res = xhr.responseText ||
xhr.responseJSON ||
xhr.responseXML;
onsuccess(res);
}
};
if(method && method.toUpperCase() == 'POST'){
params.noCACHE = false;
post = Ajax.encodeURIParams(params);
xhr.open(method, url, true);
xhr.setRequestHeader('Content-type', 'application/x-www-
form-urlencoded');
xhr.setRequestHeader('Content-length', post.length);
xhr.setRequestHeader('Connection', 'close');
}else{
method = 'GET';
xhr.open(method, url+'?'+Ajax.encodeURIParams(params),
true);
}
xhr.send(post);
}
}
No question. But these days it is typically jQuery fouling up behind
the scenes, not Prototype.
>
> I've started writing my own library, and I originally attempt to make
> it appear similar to Prototype, in the hopes that I could easily get
> the other developers off Prototype and eventually improve our site.
I sure wouldn't copy Prototype's much-maligned design.
>
> The following is the "Ajax" section of the code, and I would greatly
> appreciate (and request) critiques of the code from the group.
>
> Ajax = {
> transport : (function(){
> if(window.XMLHttpRequest){
Bad feature detection. Use typeof (or a wrapper like isHostMethod).
> return new XMLHttpRequest();
> }else if(window.ActiveXObject){
> return new ActiveXObject('Microsoft.XMLHTTP');
You need a try-catch around this (ActiveX is easily disabled) and
there are more ID's to consider.
> }else{
> return false;
> }
> })(),
>
> encodeURIParams : function(params){
> var url='',
> noCACHE = Math.random()*10e17,
Why? There are headers for this.
> key, i;
>
> for(key in params){
> if(params.hasOwnProperty(key) && key != 'noCACHE'){
Don't use hasOwnProperty as it isn't supported by Safari 2 (for
example). Use a wrapper like isOwnProperty.
> //in the case of a select multiple
> if(typeof params[key] == 'object' &&
> params[key] instanceof Array){
Don't use instanceof for this (and don't design code that must
discriminate between Array objects and Object objects). What other
sort of object could this be anyway?
> for(i = params[key].length; i--;){
> url += encodeURIComponent(key) + '=' +
> encodeURIComponent(params[key][i])+'&';
> }
>
> }else{
> url += encodeURIComponent(key) + '=' +
> encodeURIComponent(params[key])+'&';
> }
> }
> }
Faster to push the names, values and separators to an array and then
join with "".
>
> if(!params.hasOwnProperty('noCACHE')){
See above. And if it does _not_ have this property, wouldn't that
mean to cache? It's very confusing to have this magic parameter in
with the rest of the data. Also, the caching behavior for XHR GET's
varies cross-browser (IE doesn't bother to check freshness).
> url += 'noCACHE='+noCACHE;
Never do this. Certainly not for data that will be POSTed.
> }else{
> url = url.substring(0, url.length-1);
> }
>
> return url;
> },
>
> Request : function(url, params, onsuccess, method){
This is not a constructor.
> var xhr = Ajax.transport,
Do you really need a "namespace" for this?
> res = '',
> post = null;
>
> xhr.onreadystatechange = function(){
> if(onsuccess && xhr.readyState==4){
> res = xhr.responseText ||
> xhr.responseJSON ||
> xhr.responseXML;
This is bizarre.
>
> onsuccess(res);
> }
> };
>
> if(method && method.toUpperCase() == 'POST'){
> params.noCACHE = false;
What does this mean, caching is true for POST's? I doubt it. :)
> post = Ajax.encodeURIParams(params);
But you put the cache-buster BS in there!
> xhr.open(method, url, true);
> xhr.setRequestHeader('Content-type', 'application/x-www-
> form-urlencoded');
> xhr.setRequestHeader('Content-length', post.length);
> xhr.setRequestHeader('Connection', 'close');
Why add these headers?
> }else{
> method = 'GET';
> xhr.open(method, url+'?'+Ajax.encodeURIParams(params),
> true);
What if the URI already had a query part?
> }
> xhr.send(post);
Need a try-catch around that.
> }
>
> }
Will leak memory in IE too. You need to set onreadystatechange to an
empty function when done.
The people you are trying to get off Prototype will complain that you
are "reinventing the wheel". For them there are only two choices: use
a ridiculously complex and ineffectual library or write _everything_
from scratch. You need to find some middle ground (e.g. download a
solid XHR wrapper to start with).
Be careful to avoid potential circular reference of Host object to
JScript object. You have Ajax.transport -> Host object...
> encodeURIParams : function(params){
> var url='',
> noCACHE = Math.random()*10e17,
> key, i;
>
> for(key in params){
What is - params - ? Is it a native ECMAScript object? If so, and if
you're not modifying object.prototype, you can ditch the call to
hasOwnProperty.
> if(params.hasOwnProperty(key) && key != 'noCACHE'){
> //in the case of a select multiple
> if(typeof params[key] == 'object' &&
> params[key] instanceof Array){
> for(i = params[key].length; i--;){
// Move the repeated encodeURIComponent(key) outside the loop and
// save the result in a variable (like encodedKey).
> url += encodeURIComponent(key) + '=' +
> encodeURIComponent(params[key][i])+'&';
> }
>
> }else{
> url += encodeURIComponent(key) + '=' +
> encodeURIComponent(params[key])+'&';
> }
> }
> }
>
Avoid the if else. Make
> if(!params.hasOwnProperty('noCACHE')){
> url += 'noCACHE='+noCACHE;
> }else{
> url = url.substring(0, url.length-1);
> }
>
> return url;
> },
>
> Request : function(url, params, onsuccess, method){
> var xhr = Ajax.transport,
> res = '',
> post = null;
>
> xhr.onreadystatechange = function(){
I believe you have created a circular reference here.
xhr has an onreadystatechange, which has xhr.
setting xhr = null will not solve the problem here. onreadystatechange
has Ajax.transport in scope chain.
Ajax.transport -> onreadystatechange -> [[Scope]] -> Ajax.transport.
> if(onsuccess && xhr.readyState==4){
> res = xhr.responseText ||
> xhr.responseJSON ||
> xhr.responseXML;
>
> onsuccess(res);
> }
> };
Why not just use responseText?
What if responseText is ""?
What is responseJSON?
> if(method && method.toUpperCase() == 'POST'){
> params.noCACHE = false;
Caching can be useful at times. Consider an application where the user
enters home zip, looks at result, then tries work zip code, looks at
result, then re-enters home zip. The home zip being cached would be useful.
--
Garrett
comp.lang.javascript FAQ: http://jibbering.com/faq/
[...]
>
> Will leak memory in IE too. You need to set onreadystatechange to an
> empty function when done.
>
(Such as Function.prototype)
> Mychal Hackman wrote:
>> Ajax = {
>> transport : (function(){
>> if(window.XMLHttpRequest){
>> return new XMLHttpRequest();
>> }else if(window.ActiveXObject){
>> return new ActiveXObject('Microsoft.XMLHTTP');
>> }else{
>> return false;
>> }
>> })(),
>
> Be careful to avoid potential circular reference of Host object to
> JScript object. You have Ajax.transport -> Host object...
Where is the circular reference? And how would you avoid it, then?
PointedEars
--
var bugRiddenCrashPronePieceOfJunk = (
navigator.userAgent.indexOf('MSIE 5') != -1
&& navigator.userAgent.indexOf('Mac') != -1
) // Plone, register_function.js:16
It's the start of a chain.
Yes.
[xmlhttp]--readystatechange-->[anon function]-->[Activation Object]-->
[xmlhttp]
But this is unrelated to the previously mentioned (partial) chain.
>
> xhr has an onreadystatechange, which has xhr.
>
> setting xhr = null will not solve the problem here. onreadystatechange
> has Ajax.transport in scope chain.
That won't solve the problem because it will blow up in IE6. You have
to set it to a Function object (preferably an empty one).
>
> Ajax.transport -> onreadystatechange -> [[Scope]] -> Ajax.transport.
No. Ajax is global. It's not referenced by the local Activation
Object. If it were, virtually everything would leak (think about it).
>
> > if(onsuccess && xhr.readyState==4){
> > res = xhr.responseText ||
> > xhr.responseJSON ||
> > xhr.responseXML;
>
> > onsuccess(res);
> > }
> > };
>
> Why not just use responseText?
You might want the XML.
onsuccess(xhr.resonseText, xhr.responseXML);
Or just:-
onsuccess(xhr);
>
> What if responseText is ""?
Nothing good. :)
>
> What is responseJSON?
Who knows? Something Prototype does?
>
> > if(method && method.toUpperCase() == 'POST'){
> > params.noCACHE = false;
>
> Caching can be useful at times. Consider an application where the user
> enters home zip, looks at result, then tries work zip code, looks at
> result, then re-enters home zip. The home zip being cached would be useful.
Caching POST's? I don't follow. And caching GET's is a major pain
because of IE's problems. So caching XHR is best avoided.
That's an interesting idea. I've always used an empty function.
I recall having trouble finding information about the ID's, and what
the differences were.
>
> > encodeURIParams : function(params){
> > var url='',
> > noCACHE = Math.random()*10e17,
>
> Why? There are headers for this.
>
I have removed the noCACHE parts, caching will be prevented if
necessary by setting the headers server-side.
>
> Don't use hasOwnProperty as it isn't supported by Safari 2 (for
> example). Use a wrapper like isOwnProperty.
Thanks, I'll look into that.
>
> > //in the case of a select multiple
> > if(typeof params[key] == 'object' &&
> > params[key] instanceof Array){
>
> Don't use instanceof for this (and don't design code that must
> discriminate between Array objects and Object objects). What other
> sort of object could this be anyway?
>
Good question...
>
> Faster to push the names, values and separators to an array and then
> join with "".
Makes sense.
>
> > Request : function(url, params, onsuccess, method){
>
> This is not a constructor.
Sorry, I don't follow here. Can you elaborate please?
> > xhr.onreadystatechange = function(){
> > if(onsuccess && xhr.readyState==4){
> > res = xhr.responseText ||
> > xhr.responseJSON ||
> > xhr.responseXML;
>
> This is bizarre.
Agreed. Not sure where responseJSON even came from...
>
> > xhr.open(method, url, true);
> > xhr.setRequestHeader('Content-type', 'application/x-www-
> > form-urlencoded');
> > xhr.setRequestHeader('Content-length', post.length);
> > xhr.setRequestHeader('Connection', 'close');
>
> Why add these headers?
Removed last two. Obviously wasn't thinking (just following blindly).
>
>
> Will leak memory in IE too. You need to set onreadystatechange to an
> empty function when done.
Thanks again. I will have to read about circular references in IE and
try to understand this more clearly.
>
> a ridiculously complex and ineffectual library or write _everything_
> from scratch.
Writing some of this will (hopefully) give me a better understanding
of the language and improve my skills as a programmer.
There is none and there would be no way to avoid it if there was.
> Garrett Smith wrote:
>> David Mark wrote:
>> > Mychal Hackman wrote:
>> >> [...]
>> > Will leak memory in IE too. You need to set onreadystatechange to an
>> > empty function when done.
>>
>> (Such as Function.prototype)
>
> That's an interesting idea. I've always used an empty function.
There is also a subtle difference between ES3F and ES5 that challenges
whether using `Function.prototype' here is still a viable approach:
,-[ECMAScript Language Specification, Edition 3 Final]
|
| 15.3.4 Properties of the Function Prototype Object
|
| The Function prototype object is itself a Function object (its [[Class]]
| is "Function") that, when invoked, accepts any arguments and returns
| undefined.
|
| The value of the internal [[Prototype]] property of the Function prototype
| object is the Object prototype object (section 15.3.2.1).
|
| It is a function with an “empty body”; if it is invoked, it merely returns
| undefined.
|
| The Function prototype object does not have a valueOf property of its own;
| however, it inherits the valueOf property from the Object prototype
| Object.
,-[ECMAScript Language Specification, Edition 5
| ("Final final final final draft"¹)]
|
| 15.3.4 Properties of the Function Prototype Object
|
| The Function prototype object is itself a Function object (its [[Class]]
| is "Function") that, when invoked, accepts any arguments and returns
| undefined.
|
| The value of the [[Prototype]] internal property of the Function prototype
| object is the standard built-in Object prototype object (15.2.4). The
| initial value of the [[Extensible]] internal property of the Function
| prototype object is true.
|
| The Function prototype object does not have a valueOf property of its own;
| however, it inherits the valueOf property from the Object prototype
| Object.
|
| The length property of the Function prototype object is 0.
Note that the part about the empty body is gone from ES5 which means that a
ES5-conforming implementation could actually do something when this method
is called as long as it accepts any arguments and returns `undefined'.
PointedEars
___________
¹ in the title of the corresponding PDF document although not advertised as
such
[...]
> The following is the "Ajax" section of the code, and I would greatly
> appreciate (and request) critiques of the code from the group.
>
> Ajax = {
Don't make assignments to undeclared identifiers (don't forget var).
[...]
>>> Request : function(url, params, onsuccess, method){
>>> var xhr = Ajax.transport,
>>> res = '',
>>> post = null;
>>> xhr.onreadystatechange = function(){
>> I believe you have created a circular reference here.
>
> Yes.
>
> [xmlhttp]--readystatechange-->[anon function]-->[Activation Object]-->
> [xmlhttp]
>
> But this is unrelated to the previously mentioned (partial) chain.
>
There are two references to the XMLHttpRequest object: xhr and
Ajax.transport. Both are in scope of the function that is assigned to
onreadystatechange.
>> xhr has an onreadystatechange, which has xhr.
>>
>> setting xhr = null will not solve the problem here. onreadystatechange
>> has Ajax.transport in scope chain.
>
> That won't solve the problem because it will blow up in IE6. You have
> to set it to a Function object (preferably an empty one).
>
Setting xhr = null won't blow up in IE. Setting xhr.onreadystatechange =
null will, but setting xhr = null won't.
Setting xhr won't break the circle, either. There is still
Ajax.transport in scope of the function assigned to xhr.onreadystatechange.
>> Ajax.transport -> onreadystatechange -> [[Scope]] -> Ajax.transport.
>
> No. Ajax is global. It's not referenced by the local Activation
> Object. If it were, virtually everything would leak (think about it).
>
Identifier Ajax does not need to be referenced by the local activation
object. It is in the scope chain.
For example, if the onreadystatechange has an eval:-
xhr.onreadystatechange = function(){
if(Math.random > .999) {
eval("alert(typeof Ajax.transport)");
}
}
- it would still have to look up the scope chain and find
Ajax.transport. It makes no difference if the Ajax object is global. The
Ajax identifier is in the scope chain of the function assigned to
onreadystatechange.
The onreadystatechange has Ajax.transport and Ajax.transport has
onreadystatechange.
>>> if(onsuccess && xhr.readyState==4){
>>> res = xhr.responseText ||
>>> xhr.responseJSON ||
>>> xhr.responseXML;
>>> onsuccess(res);
>>> }
>>> };
>> Why not just use responseText?
>
> You might want the XML.
>
> onsuccess(xhr.resonseText, xhr.responseXML);
>
> Or just:-
>
> onsuccess(xhr);
>
Passing responseXML when responseText is empty string would be an
unlikely problem case for the caller to handle. Best avoid that.
[...]
It is an odd design to have a globally accessible Ajax.transport object,
and then pass that very same object to onsuccess. I think the global
object should be gotten rid of.
IE6 (and 7?) throws errors when calling send on an XHR that has been
opened. TO reuse the object, it is necessary to fist call the xhr's
abort method.
>>> if(method && method.toUpperCase() == 'POST'){
>>> params.noCACHE = false;
>> Caching can be useful at times. Consider an application where the user
>> enters home zip, looks at result, then tries work zip code, looks at
>> result, then re-enters home zip. The home zip being cached would be useful.
>
> Caching POST's? I don't follow. And caching GET's is a major pain
> because of IE's problems. So caching XHR is best avoided.
I misread. Caching POST would be odd.
I try to creating references from host object to jscript object, in
general.
To avoid a leak, either don't create one or break the chain between host
object to jscript object.
No, actually a circular reference does exist below. One of the
identifiers in scope of that reference is Ajax.transport.
>>> Request : function(url, params, onsuccess, method){
>> This is not a constructor.
>
> Sorry, I don't follow here. Can you elaborate please?
By convention, upper case function names are reserved for constructors.
Gregor
That's correct. I misread.
>
> Setting xhr won't break the circle, either. There is still
> Ajax.transport in scope of the function assigned to xhr.onreadystatechange.
But setting onreadystatechange to an empty Function will. That's the
most important point.
>
> >> Ajax.transport -> onreadystatechange -> [[Scope]] -> Ajax.transport.
>
> > No. Ajax is global. It's not referenced by the local Activation
> > Object. If it were, virtually everything would leak (think about it).
>
> Identifier Ajax does not need to be referenced by the local activation
> object. It is in the scope chain.
That's irrelevant. There's no circular reference in that case. ;)
>
> For example, if the onreadystatechange has an eval:-
>
> xhr.onreadystatechange = function(){
> if(Math.random > .999) {
> eval("alert(typeof Ajax.transport)");
> }
>
> }
>
> - it would still have to look up the scope chain and find
> Ajax.transport. It makes no difference if the Ajax object is global.
Of course it does. Diagram it.
> The
> Ajax identifier is in the scope chain of the function assigned to
> onreadystatechange.
As mentioned, that is irrelevant to the memory leak consideration.
>
> The onreadystatechange has Ajax.transport and Ajax.transport has
> onreadystatechange.
>
>
>
>
>
> >>> if(onsuccess && xhr.readyState==4){
> >>> res = xhr.responseText ||
> >>> xhr.responseJSON ||
> >>> xhr.responseXML;
> >>> onsuccess(res);
> >>> }
> >>> };
> >> Why not just use responseText?
>
> > You might want the XML.
>
> > onsuccess(xhr.resonseText, xhr.responseXML);
>
> > Or just:-
>
> > onsuccess(xhr);
>
> Passing responseXML when responseText is empty string would be an
> unlikely problem case for the caller to handle. Best avoid that.
>
> [...]
>
> It is an odd design to have a globally accessible Ajax.transport object,
> and then pass that very same object to onsuccess. I think the global
> object should be gotten rid of.
Certainly.
>
> IE6 (and 7?) throws errors when calling send on an XHR that has been
> opened. TO reuse the object, it is necessary to fist call the xhr's
> abort method.
>
> >>> if(method && method.toUpperCase() == 'POST'){
> >>> params.noCACHE = false;
> >> Caching can be useful at times. Consider an application where the user
> >> enters home zip, looks at result, then tries work zip code, looks at
> >> result, then re-enters home zip. The home zip being cached would be useful.
>
> > Caching POST's? I don't follow. And caching GET's is a major pain
> > because of IE's problems. So caching XHR is best avoided.
>
> I misread. Caching POST would be odd.
Yes.
But it does _not_ occur because of this potential (which is never
realized). That's a very important distinction.
>
> I try to creating references from host object to jscript object, in
> general.
To avoid? Yes, I do too. In light of IE's problems in this area,
it's the only sane approach. Just like you should avoid overloading
due to language constraints. Nevertheless, both of those bee-lines to
disaster are buzzing with activity (pick up any library or framework).
>
> To avoid a leak, either don't create one or break the chain between host
> object to jscript object.
Exactly.
As mentioned, that doesn't matter. Think about it (or draw a diagram
if in doubt).
var Ajax = (function(){
var getXhr = (function(){
if(typeof window.XMLHttpRequest != 'undefined'){
return new XMLHttpRequest();
//lookup other progIds
}else if(typeof window.ActiveXObject != 'undefined'){
try{
return new ActiveXObject('Microsoft.XMLHTTP');
}catch(e){ return false; }
}else{
return false;
}
})(),
encodeURIParams = function(params){
var url = [], key, i;
for(key in params){
//in the case of a select multiple
if(typeof params[key] == 'object'){
for(i = params[key].length; i--;){
url.push(encodeURIComponent(key), '=',
encodeURIComponent(params[key][i]), '&');
}
}else{
url.push(encodeURIComponent(key), '=',
encodeURIComponent(params[key]), '&');
}
}
return url.join('');
},
Request = function(url, params, onsuccess, method){
var xmlHttp = getXhr,
post = null;
xmlHttp.onreadystatechange = function(){
if(onsuccess && xmlHttp.readyState==4){
onsuccess(xmlHttp.responseText, xmlHttp.responseXML);
xmlHttp.onreadystatechange = function(){};
}
};
if(method && method.toUpperCase() == 'POST'){
post = encodeURIParams(params);
xmlHttp.open(method, url, true);
xmlHttp.setRequestHeader('Content-type', 'application/x-
www-form-urlencoded');
}else{
method = 'GET';
//need to deal with uris with existing query
xmlHttp.open(method, url+'?'+encodeURIParams(params),
true);
}
try{
xmlHttp.send(post);
}catch(e){}
};
return { Request : Request };
})();
> xmlHttp.onreadystatechange = function(){
> xmlHttp.onreadystatechange = function(){};
That isn't break circular reference in the way you do it. You again
form closure and you produce long Scope Chain. That empty function
refer from [[scope]] property AO/VO associated with execution context
in wich you create.
AO/VO => Request EC
^
AO/VO => xmlHttp.onreadystatechange EC
^
[[scope]] of xmlHttp.onreadystatechange = function(){};
Read that article <URL: http://www.jibbering.com/faq/faq_notes/closures.html>
Correct.
var fn = function() {};
...
(function() {
...
xmlHttp.onreadystatechange = fn; // fn defined outside of closure
As (window.someThing) can return an object, null or undefined (on the
browser-side), it's irrelevant to check for 'typeof window.someThing'.
So,
if (typeof window.XMLHttpRequest != 'undefined') {}
is equivalent to just
if (window.XMLHttpRequest) {} // null, undefined and '' evaluate to
false.
> //lookup other progIds
> }else if(typeof window.ActiveXObject != 'undefined'){
> try{
> return new ActiveXObject('Microsoft.XMLHTTP');
> }catch(e){ return false; }
Firstly, lose that "return false" in the catch(e) { } block. Secondly,
you've got to try "Msxml2.XMLHTTP" too, not just "Microsoft.XMLHTTP".
I suggest checking for that in the first place, because IE is *still*
the most used browser (I hat to admit that). For instance:
var getXhr = (function() {
var activeXmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
if (window.ActiveXObject) {
for (var i = 0; i < activeXmodes.length; i++) {
try { return new ActiveXObject(activeXmodes[i]); }
catch (e) { } // Ignores any error.
}
} else if (window.XMLHttpRequest) { // Firefox, Opera 8.0+, Safari.
return new XMLHttpRequest();
} else {
// this browser doesn't support AJAX.
return false;
}
})(),
--
JR
Never detect host objects by type conversion. Some of them blow
up. :)
>
> > //lookup other progIds
> > }else if(typeof window.ActiveXObject != 'undefined'){
> > try{
> > return new ActiveXObject('Microsoft.XMLHTTP');
> > }catch(e){ return false; }
>
> Firstly, lose that "return false" in the catch(e) { } block. Secondly,
> you've got to try "Msxml2.XMLHTTP" too, not just "Microsoft.XMLHTTP".
As noted.
> I suggest checking for that in the first place, because IE is *still*
> the most used browser (I hat to admit that). For instance:
>
> var getXhr = (function() {
> var activeXmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
> if (window.ActiveXObject) {
No. IE has supported XMLHttpRequest since version 7, some users have
ActiveX disabled for them and some agents have dummy global
ActiveXObject properties to prevent unnecessary exclusion by browser
sniffing scripts.
Excellent post. I hope OP understand your post. If you wrote:
(function(){
var fn = function() {};
Circular reference will be still there.
getXhr -> onreadystatechange -> fn -> AO -> getXhr
I have only one question. Why you suggest here to use function
expression and resolve `fn' identifier form Scope Chain instead of
`Function' constructor to create object with [[scope]] property which
refer Global Object? He can do it:
xmlHttp.onreadystatechange = Function();
> (function(){
> var fn = function() {};
>
> Circular reference will be still there.
Yes because the function is created within the scope of
xmlHttp.onreadystatechange. If I understand correctly, the situation
would be exactly the same as before.
> if(typeof window.XMLHttpRequest != 'undefined'){
> return new XMLHttpRequest();
Why you mixin `11.2.1 Property Accessors` and `11.1.2 Identifier
Reference`? That can be very hard to debug. See below:
(function(){
var XMLHttpRequest;
if (window.XMLHttpRequest)
{
new XMLHttpRequest(); //TypeError for 11.2.2 point 3
}
}());
Should be:
new window.XMLHttpRequest();
> onsuccess(xmlHttp.responseText, xmlHttp.responseXML);
onsuccess should be test for internal [[Call]] before you called.
if (typeof onsuccess == 'function')
{
onsuccess();
}
Overkill. Documentation should specify the types of expected
arguments.
I can't remember a single case in which window.XMLHttpRequest or
window.ActiveXObject should 'blow up'. I'm sitting on a bomb and
didn't know about that (just kidding).
> > > //lookup other progIds
> > > }else if(typeof window.ActiveXObject != 'undefined'){
> > > try{
> > > return new ActiveXObject('Microsoft.XMLHTTP');
> > > }catch(e){ return false; }
>
> > Firstly, lose that "return false" in the catch(e) { } block. Secondly,
> > you've got to try "Msxml2.XMLHTTP" too, not just "Microsoft.XMLHTTP".
>
> As noted.
>
> > I suggest checking for that in the first place, because IE is *still*
> > the most used browser (I hat to admit that). For instance:
>
> > var getXhr = (function() {
> > var activeXmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
> > if (window.ActiveXObject) {
>
> No. IE has supported XMLHttpRequest since version 7
Ok, I forgot that IE's native XMLHTTP support is enabled by default.
But anyone can disable it, so... Anyway, if even MS checks for the
native object as a first step, I won't be the 'smart alec' here saying
the contrary:
http://msdn.microsoft.com/en-us/library/ms537505(VS.85).aspx
> [...] some users have
> ActiveX disabled for them and some agents have dummy global
> ActiveXObject properties to prevent unnecessary exclusion by browser
> sniffing scripts.
I didn't follow what's the problem?
--
JR
[...]
> I have only one question. Why you suggest here to use function
> expression and resolve `fn' identifier form Scope Chain instead of
> `Function' constructor to create object with [[scope]] property which
> refer Global Object? He can do it:
>
> xmlHttp.onreadystatechange = Function();
The built-in Function constructor uses much less efficient algorithm for
building a function. A straight function expression is much simpler.
xmlHttp.onreadystatechange = noop;
If you get tired of defining a noop in all your modules, you want to
reuse a global function with global scope.
Function.prototype is a globally accessible function with global scope.
Function.prototype implements [[Call]] but not [[Construct]].
> Never detect host objects by type conversion. Some of them blow
> up. :)
Yes, i agree with you especially in ToPrimitive. But for ToBoolean are
have any example where ToBoolean can be harmful. I think ToBoolean and
ToObject, check the internal type of object.
ToBoolean:
if (Type(value) === Object)
{
return true;
}
ToObject:
if (Type(value) === Object)
{
return value;
}
The Function constructor is inherently evil (avoid whenever
possible). Calling it without the new Operator is bad form. Creating
a new function each time through is inefficient.
> Overkill. Documentation should specify the types of expected
> arguments.
You are absolutely right. And you use absolutely right term. Thanks
for correction ;)
Do you keep a running list of host objects that do? It's best to
avoid the issue.
>
>
>
> > > > //lookup other progIds
> > > > }else if(typeof window.ActiveXObject != 'undefined'){
> > > > try{
> > > > return new ActiveXObject('Microsoft.XMLHTTP');
> > > > }catch(e){ return false; }
>
> > > Firstly, lose that "return false" in the catch(e) { } block. Secondly,
> > > you've got to try "Msxml2.XMLHTTP" too, not just "Microsoft.XMLHTTP".
>
> > As noted.
>
> > > I suggest checking for that in the first place, because IE is *still*
> > > the most used browser (I hat to admit that). For instance:
>
> > > var getXhr = (function() {
> > > var activeXmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
> > > if (window.ActiveXObject) {
>
> > No. IE has supported XMLHttpRequest since version 7
>
> Ok, I forgot that IE's native XMLHTTP support is enabled by default.
> But anyone can disable it, so...
You need to use a try-catch with it as well.
> Anyway, if even MS checks for the
> native object as a first step, I won't be the 'smart alec' here saying
> the contrary:
Huh?
>
> http://msdn.microsoft.com/en-us/library/ms537505(VS.85).aspx
What is it you see there?
>
> > [...] some users have
> > ActiveX disabled for them and some agents have dummy global
> > ActiveXObject properties to prevent unnecessary exclusion by browser
> > sniffing scripts.
>
> I didn't follow what's the problem?
>
Some agents implement window.ActiveXObject, but it doesn't do anything.
> Overkill. Documentation should specify the types of expected
> arguments.
Again here you are correct, but if it that argument is optional, i
think must be tested for [[Call]] before called. How you think about
that case and design with optiona* arguments?
Yes. There are lots of examples (e.g. all ActiveX methods):-
if (window.external.addFavorite) {
...and properties under some circumstances:-
if (el.offsetParent) {
...will also blow up. The indicator is a typeof result of "unknown".
> Yes. There are lots of examples (e.g. all ActiveX methods):-
No. From that i can see they have own implementation of internal
[[Get]] and [[Put]] methods.
var a = window.external.addFavorite;
No type conversion but error is still there. Interesting in that part
is typeof.
| 1. Evaluate UnaryExpression.
| [...]
| 4. Call GetValue(Result(1))
So here again we have implicit invocation of [[Get]] method of that
`object'. But i m not sure people of Microsoft implement in that way.
How you think about that?
I think they are nuts, but host objects can be implemented any way the
developers see fit.
I detect features with isHostMethod/isHostObjectProperty so I don't
have to keep track of their nonsense (and so I have a single line of
code to update in case they decide to change the rules after ten
years).
You seem to have a misunderstanding of how scope chain and identifier
resolution works.
The next object in scope chain of the function assigned in
onreadystatechange is the function object assigned to Ajax.Request the
next object in the scope chain after that is the containing scope, which
contains the Ajax object itself.
onreadystatechange handler Scope --> Ajax.Request Scope --> containing
scope (global?)
In fact, Ajax object does not have to be global at all.
>
>> The
>> Ajax identifier is in the scope chain of the function assigned to
>> onreadystatechange.
>
> As mentioned, that is irrelevant to the memory leak consideration.
>
Ah, not that is you failing to see the relevance, not me writing
irrelevance.
The Ajax.transport is in scope of the function assigned. If, for
example, the anonymous function body set xhr = null, then there would be
still one remaining link to the XMLHttpRequest object. That reference is
Ajax.transport.
[Global Scope]
Variable: Ajax
|
|
[Ajax.Request Scope]
Variable: xhr, res, post, url, params, onsuccess, method
|
|
[onreadystatechange handler Scope]
Variable: {empty}
Start from the bottom of the diagram, in onreadystatechange handler
Scope. The scope chain includes Ajax.Request Scope, and then the scope
above that (global, apparently, but it doesn't really matter). The last
object on the scope chain has a reference to Ajax.transport.
Ajax.transport is in the scope chain of onreadystatechange.
>> The onreadystatechange has Ajax.transport and Ajax.transport has
>> onreadystatechange.
>>
No, you seem to be confused about how circular references are
created. ;)
[...]
>
> > As mentioned, that is irrelevant to the memory leak consideration.
>
> Ah, not that is you failing to see the relevance, not me writing
> irrelevance.
No, you are just confusing the uninitiated with your confusion.
>
> The Ajax.transport is in scope of the function assigned. If, for
> example, the anonymous function body set xhr = null, then there would be
> still one remaining link to the XMLHttpRequest object. That reference is
> Ajax.transport.
> [Global Scope]
> Variable: Ajax
> |
> |
> [Ajax.Request Scope]
> Variable: xhr, res, post, url, params, onsuccess, method
> |
> |
> [onreadystatechange handler Scope]
> Variable: {empty}
>
> Start from the bottom of the diagram, in onreadystatechange handler
> Scope. The scope chain includes Ajax.Request Scope, and then the scope
> above that (global, apparently, but it doesn't really matter). The last
> object on the scope chain has a reference to Ajax.transport.
> Ajax.transport is in the scope chain of onreadystatechange.
>
You are missing a link in the chain (Activation Object). Without it,
there's no linkage between the Function object and the globally
declared Ajax object.
[...]
> Note that the part about the empty body is gone from ES5 which means that a
> ES5-conforming implementation could actually do something when this method
> is called as long as it accepts any arguments and returns `undefined'.
So what? They removed the obvious.
A legal implementation extension might be to add behavior to
Function.prototype that causes system side effects. The same is true for
any built-in function (parseInt, etc).
[...]
>
> No, you are just confusing the uninitiated with your confusion.
>
You wrote:
| Ajax is global. It's not referenced by the local Activation
| Object. If it were, virtually everything would leak (think about it).
The Ajax object is not a member of the containing scope, but it is a
member of an object in the containing scope's containing scope.
So rather than xhr -> onreadystatechange function [[Scope]] -> function
[[Scope]] -> xhr, we have:
xhr -> onreadystatechange function [[Scope]] -> Ajax.Request function
[[Scope]] --> global [[Scope]].
>
> You are missing a link in the chain (Activation Object). Without it,
> there's no linkage between the Function object and the globally
> declared Ajax object.
The variable/activation object is written as "Variable:". In the
onreadystatechange handler, it is empty, written as "(empty)".
> Thomas 'PointedEars' Lahn wrote:
>> David Mark wrote:
>>> Garrett Smith wrote:
>>>> David Mark wrote:
>>>>> Mychal Hackman wrote:
>>>>>> [...]
>>>>> Will leak memory in IE too. You need to set onreadystatechange to an
>>>>> empty function when done.
>>>> (Such as Function.prototype)
>>> That's an interesting idea. I've always used an empty function.
>>
>> There is also a subtle difference between ES3F and ES5 that challenges
>> whether using `Function.prototype' here is still a viable approach:
>
> [...]
>
>> Note that the part about the empty body is gone from ES5 which means that
>> a ES5-conforming implementation could actually do something when this
>> method is called as long as it accepts any arguments and returns
>> `undefined'.
>
> So what? They removed the obvious.
Nonsense.
> A legal implementation extension might be to add behavior to
> Function.prototype that causes system side effects.
Which it why it should not be used anymore.
> The same is true for any built-in function (parseInt, etc).
You don't know what you are talking about.
PointedEars
--
Danny Goodman's books are out of date and teach practices that are
positively harmful for cross-browser scripting.
-- Richard Cornford, cljs, <cife6q$253$1$8300...@news.demon.co.uk> (2004)
Which is irrelevant for our purposes. You are missing the obvious.
>
> So rather than xhr -> onreadystatechange function [[Scope]] -> function
> [[Scope]] -> xhr, we have:
>
> xhr -> onreadystatechange function [[Scope]] -> Ajax.Request function
> [[Scope]] --> global [[Scope]].
Nope. Not a circular reference.
Think about what you quoted above. _Everything_ would leak if
affected implementations were foolish enough to maintain links to the
Global Object for every object. They obviously have no need to do
that and they don't do it, so it doesn't matter.
What is nonsense? And why?
>> A legal implementation extension might be to add behavior to
>> Function.prototype that causes system side effects.
>
> Which it why it should not be used anymore.
>
>> The same is true for any built-in function (parseInt, etc).
>
> You don't know what you are talking about.
>
Do you have a reason for believing that, or is it more hot air?
The global scope object is on the scope chain.
Suppose the entire object were not global, but hidden.
(function(){
var Ajax = {
transport : function(){
if(window.XMLHttpRequest){
return new XMLHttpRequest();
}else if(window.ActiveXObject){
return new ActiveXObject('Microsoft.XMLHTTP');
}else{
return false;
}
})(),
Request : function(url, params, onsuccess, method){
var xhr = Ajax.transport,
res = '',
post = null;
xhr.onreadystatechange = function(){
if(onsuccess && xhr.readyState==4){
res = xhr.responseText ||
xhr.responseJSON ||
xhr.responseXML;
onsuccess(res);
}
};
//...
}
}
Now suppose for a second that a local identifer xhr had *not* been used,
but instead, that Ajax.transport had been used. Would there be a
circular reference?
Request : function(url, params, onsuccess, method){
var res = '',
post = null;
Ajax.transport.onreadystatechange = function(){
if(onsuccess && xhr.readyState==4){
res = xhr.responseText ||
xhr.responseJSON ||
xhr.responseXML;
onsuccess(res);
}
};
// ...
> http://www.jibbering.com/faq/faq_notes/closures.html#clMem
Good:
| A circular reference is when two or more objects refer to each other
| in a way that can be followed and lead back to the starting point.
Is there a circular reference in the above example?
Ajax.transport -> XHR_OBJECT -> onreadystatechange -> function[[Scope]]
- > function[[scope]] -> Ajax.transport.
As long as Ajax.transport exists on the scope chain, it can't be collected.
The Ajax object itself could be collected, so long as there were no
other references to it. The transport property would be collectible too,
but that has a reference and that reference is a Host object. Now that
host object has a property onreadystatechange, which points to a
function, and that function has a scope chain, that scope chain has, at
the end, a member object with the member Ajax.
That's obviously different. As with the viewport discussion (and
attributes before that) you've latched on to the wrong end of the
stick and are beating the thread to death with it. :(
[...]
The error is unrelated to type conversion. The typeof operator works on
reference and so it seems that typeof can't get a reference, then it
returns "unknown".
Wise advice. Thanks.
> > > > I suggest checking for that in the first place, because IE is *still*
> > > > the most used browser (I hat to admit that). For instance:
>
> > > > var getXhr = (function() {
> > > > var activeXmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
> > > > if (window.ActiveXObject) {
>
> > > No. IE has supported XMLHttpRequest since version 7
>
> > Ok, I forgot that IE's native XMLHTTP support is enabled by default.
> > But anyone can disable it, so...
>
> You need to use a try-catch with it as well.
>
> > Anyway, if even MS checks for the
> > native object as a first step, I won't be the 'smart alec' here saying
> > the contrary:
>
> Huh?
>
>
>
> >http://msdn.microsoft.com/en-us/library/ms537505(VS.85).aspx
>
> What is it you see there?
I see the following text [excerpt]:
"XMLHTTP in IE7 vs. IE6
The native implementation of the XMLHTTP object is designed with cross-
browser compatibility in mind. With just a bit of script, it is easy
to build a function that works with either version of Internet
Explorer, or any browser that supports XMLHTTP. See XMLHttpRequest for
complete documentation and examples.
var xmlHttp = null;
if (window.XMLHttpRequest) {
// If IE7, Mozilla, Safari, and so on: Use native object.
xmlHttp = new XMLHttpRequest();
}
else
{
if (window.ActiveXObject) {
// ...otherwise, use the ActiveX control for IE5.x and IE6.
xmlHttp = new ActiveXObject('MSXML2.XMLHTTP.3.0');
}
}
Internet Explorer 7 supports the legacy implementation of XMLHTTP in
addition to the new native object, so pages currently using the
ActiveX control do not have to be rewritten. However, it is more
efficient to create a native scriptable object than to create an
ActiveX object. This is especially beneficial to those AJAX
applications that create a new XMLHTTP object for each request.
The native object also supports the use of expandos (custom
properties), and properly recognizes the 'this' notation of
Javascript.
ActiveX vs. XMLHTTP
Users and organizations that choose to disallow ActiveX controls can
still use XMLHTTP-based Web applications in Internet Explorer 7.
However, native XMLHTTP support can be disabled from the Advanced
settings tab of the Internet Options dialog box.
Internet Options Dialog
Clients can configure their own policy and simultaneously retain
functionality across key AJAX scenarios. By default, the native
implementation of XMLHTTP is enabled for all MSHTML hosts; however,
individual host applications can choose to disable XMLHTTP with the
FEATURE_XMLHTTP feature control key. An organization can use Group
Policy to disable XMLHTTP for all users of its network.
If native XMLHTTP has been disabled, developers can override the
XMLHttpRequest property of the window object with the MSXML-XMLHTTP
control, unless ActiveX has also been disabled, as in the following
example.
if (!window.XMLHttpRequest) {
window.XMLHttpRequest = function() {
try {
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
}
catch (ex) {
return null;
}
}
}
"
> > > [...] some users have
> > > ActiveX disabled for them and some agents have dummy global
> > > ActiveXObject properties to prevent unnecessary exclusion by browser
> > > sniffing scripts.
>
> > I didn't follow what's the problem?
>
> Some agents implement window.ActiveXObject, but it doesn't do anything.
Thanks for that!
Cheers,
JR
So, they don't know how to detect their own hosts' features.
> // If IE7, Mozilla, Safari, and so on: Use native object.
> xmlHttp = new XMLHttpRequest();}
>
> else
> {
> if (window.ActiveXObject) {
And so on.
> // ...otherwise, use the ActiveX control for IE5.x and IE6.
> xmlHttp = new ActiveXObject('MSXML2.XMLHTTP.3.0');
No try-catch either. :)
> }
>
> }
>
> Internet Explorer 7 supports the legacy implementation of XMLHTTP in
> addition to the new native object, so pages currently using the
> ActiveX control do not have to be rewritten. However, it is more
> efficient to create a native scriptable object than to create an
> ActiveX object. This is especially beneficial to those AJAX
> applications that create a new XMLHTTP object for each request.
Yes. Lot of those. :(
>
> The native object also supports the use of expandos (custom
> properties), and properly recognizes the 'this' notation of
> Javascript.
>
> ActiveX vs. XMLHTTP
> Users and organizations that choose to disallow ActiveX controls can
> still use XMLHTTP-based Web applications in Internet Explorer 7.
Yes.
> However, native XMLHTTP support can be disabled from the Advanced
> settings tab of the Internet Options dialog box.
Yes.
>
[...]
>
> If native XMLHTTP has been disabled, developers can override the
> XMLHttpRequest property of the window object with the MSXML-XMLHTTP
> control, unless ActiveX has also been disabled, as in the following
> example.
>
> if (!window.XMLHttpRequest) {
> window.XMLHttpRequest = function() {
> try {
> return new ActiveXObject('MSXML2.XMLHTTP.3.0');
> }
> catch (ex) {
> return null;
> }
> }}
Awful.
Is it a difference that would create a memory leak?
If so, can you explain how?
You are going around in circles. :)
http://www.jibbering.com/faq/faq_notes/closures.html#clMem
I already diagrammed the OP's case (as I've done for a thousand
similar cases in the last few years). It's the same diagram for your
latest example. Your previous example had no such (valid) diagram.
> > No, you are just confusing the uninitiated with your confusion.
>
> You wrote:
>
> | Ajax is global. It's not referenced by the local Activation
> | Object. If it were, virtually everything would leak (think about it).
>
> The Ajax object is not a member of the containing scope, but it is a
> member of an object in the containing scope's containing scope.
If you are correct:
window.onload = function(){};
That will be produce circular reference. If `window' host object is
property of Global Object diagram is:
Global Object -> window -> onload -> AF[[scope]] -> Global Object
And that's just the tip of the (hypothetical) iceberg. Think about
what other host objects are in the Global scope.
So the hypothesis (which is not new) is obviously bunk.
[...]
>>> That's obviously different.
>> Is it a difference that would create a memory leak?
>>
>> If so, can you explain how?
>
> You are going around in circles. :)
>
Either you can explain the difference or you cannot. So far, you have
not been able to explain the difference.
> http://www.jibbering.com/faq/faq_notes/closures.html#clMem
>
> I already diagrammed the OP's case (as I've done for a thousand
> similar cases in the last few years). It's the same diagram for your
> latest example. Your previous example had no such (valid) diagram.
A diagram does not need use the same notation that you used to be valid
and it is indeed valid, just not apparently understandable by you.
Either you didn't understand the explanation(s) or you are trying to
save face (and that ship has sailed). There is a third possibility
that you want me to spoon-feed it to you, but baiting won't make that
happen.
>
> >http://www.jibbering.com/faq/faq_notes/closures.html#clMem
>
> > I already diagrammed the OP's case (as I've done for a thousand
> > similar cases in the last few years). It's the same diagram for your
> > latest example. Your previous example had no such (valid) diagram.
>
> A diagram does not need use the same notation that you used to be valid
> and it is indeed valid, just not apparently understandable by you.
Who said anything about notation? Your diagram makes no sense in any
notation (that's the point).
Is anyone else here confused? We've been over these circular
reference patterns ad nauseam, but if this is not an isolated case of
confusion, I guess we can go over it again...
> On Dec 23, 3:57 pm, JR <groups_j...@yahoo.com.br> wrote:
>> On Dec 23, 3:59 pm, Mychal Hackman <mycha...@gmail.com> wrote:
>> > //lookup other progIds
>> > }else if(typeof window.ActiveXObject != 'undefined'){
>> > try{
>> > return new ActiveXObject('Microsoft.XMLHTTP');
>> > }catch(e){ return false; }
>>
>> Firstly, lose that "return false" in the catch(e) { } block. Secondly,
>> you've got to try "Msxml2.XMLHTTP" too, not just "Microsoft.XMLHTTP".
>
> As noted.
What would one need "Msxml2.XMLHTTP" for?
PointedEars
--
var bugRiddenCrashPronePieceOfJunk = (
navigator.userAgent.indexOf('MSIE 5') != -1
&& navigator.userAgent.indexOf('Mac') != -1
) // Plone, register_function.js:16
Right.
In IE, the global variable object is implemented as a JScript object,
and the global object is implemented by the host. That is a bug of IE,
explained in Eric Lippert's blog entry many years ago.
// IE global obj.
Ajax = { ... }
// Global variable obj.
var Ajax = { ... }
COM_OBJECT -> JSCRIPT_OBJ_CHAIN -> COM_OBJ
The global object has to have some sort of tear down to remove break the
chain, as above, so that afte page tear down, there would be:
GLOBAL_COM_OBJ -|| JSCRIPT_OBJ_CHAIN -> COM_OBJ -|| GLOBAL_COM_OBJ
"-||" means chain broken.
And the same for the global variable object:
GLOBAL_SCOPE_OBJECT -|| JSCRIPT_OBJ_CHAIN -> COM_OBJ ->
JSCRIPT_SCOPE_CHAIN -|| GLOBAL_SCOPE_OBJECT
A leak won't occur unless the chain is broken. If the chain is broken
automatically for global object and global variable object then that
leak won't occur.
Someone from Microsoft could probably explain how IE implements page
tear down.
[1]http://blogs.msdn.com/ericlippert/archive/2005/05/04/414684.aspx
Wrong, no leak. Give it up.
It must be frustrating not being able to explain things.
> On Dec 23, 10:38 pm, JR <groups_j...@yahoo.com.br> wrote:
>> Internet Explorer 7 supports the legacy implementation of XMLHTTP in
>> addition to the new native object, so pages currently using the
>> ActiveX control do not have to be rewritten. However, it is more
>> efficient to create a native scriptable object than to create an
>> ActiveX object. This is especially beneficial to those AJAX
>> applications that create a new XMLHTTP object for each request.
>
> Yes. Lot of those. :(
While it might be more efficient to use the new "native object" (it isn't
one) over the ActiveX object, it is less helpful, and not suited for a
general-purpose XHR library: by contrast, it does not support `file://' URIs
and it is not backwards-compatible.
PointedEars
--
realism: HTML 4.01 Strict
evangelism: XHTML 1.0 Strict
madness: XHTML 1.1 as application/xhtml+xml
-- Bjoern Hoehrmann
According to Adam Wiener, Lead Program Manager for Data
Programmability/XML Technologies (Microsoft), the “Microsoft”
namespace is actually older and is only implemented in MSXML3 for
legacy support. So “msxml2” namespace should be used for
instantiating objects.
--
JR
`file://' URIs are blocked by default in FF 3 for instance
(about:config, security.fileuri.strict_origin_policy).
-- JR
> Asen Bozhilov wrote:
>> David Mark wrote:
>>> Yes. There are lots of examples (e.g. all ActiveX methods):-
>> No. From that i can see they have own implementation of internal
>> [[Get]] and [[Put]] methods.
>>
>> var a = window.external.addFavorite;
>>
>> No type conversion but error is still there. Interesting in that part
>> is typeof.
>
> [...]
>
> The error is unrelated to type conversion.
True. It is related to the implementation-dependent [[Get]] method of the
host object that `window.external' refers to, which is triggered by the
property accessor (ES3F/ES5, 11.2.1).
> The typeof operator works on reference and so it seems that typeof can't
> get a reference, then it returns "unknown".
"unknown" is one possible implementation-dependent value returned by the
algorithm for the `typeof' operator as triggered by its step 5 (ES3F,
11.4.3) or 3 (ES5, 11.4.3). It does not require the type of the operand to
be a non-Reference, and it is not likely for a property accessor to a
method to result in such a value.
PointedEars
--
Prototype.js was written by people who don't know javascript for people
who don't know javascript. People who don't know javascript are not
the best source of advice on designing systems that use javascript.
-- Richard Cornford, cljs, <f806at$ail$1$8300...@news.demon.co.uk>
Go back and read my posts to this thread. I explained your
misunderstanding at least twice. Baiting won't garner a third. ;)
> "unknown" is one possible implementation-dependent value returned by the
> algorithm for the `typeof' operator as triggered by its step 5 (ES3F,
> 11.4.3) or 3 (ES5, 11.4.3). It does not require the type of the operand to
> be a non-Reference, and it is not likely for a property accessor to a
> method to result in such a value.
All right but step 4 in 11.4.3 ECMA3 explicit say:
| 4. Call GetValue(Result(1)).
So again we have implicit invocation of [[Get]]. They have error
handling in typeof implementation or treat that for special case in
theirs [[Get]] method. Can you give a clear explanation or better
supposition?
Merry Christmas.
That does not answer my question, though.
And since "Microsoft.XMLHTTP" apparently means the same as
"MSXML2.XMLHTTP.2.0", what he suggested in 2006 did not make sense then and
is not making more sense now. Replacing the identifier with
"MSXML2.XMLHTTP.3.0" as he suggests would break compatibility. Adding the
identifier would decrease efficiency for older implementations at no
advantage. Surely you are not suggesting Microsoft would deliberately
break backwards-compatibility?
According to MSDN (http://msdn.microsoft.com/en-us/library/
cc507441%28VS.85%29.aspx)
- "MSXML 3.0 and prior versions of MSXML are version independent.
MSXML 3.0 was the last version of MSXML to support version-independent
CLSIDs and ProgIDs."
- "MSXML 3.0 is supported in Windows Vista; Windows Server 2003;
Windows XP; Windows 2000; Windows 98; Windows Millenium Edition (ME)."
You seem to be mistaking MSXML versions with IE versions. You may look
for msxml3.dll (v. 3.0) in your Windows\System32 folder to verify the
above information.
Since the OP's original code 'try-catch' only version 2.0
(Microsoft.XMLHTTP), I think his code needs to test for version 3.0 as
well (MSXML2.XMLHTTP).
--
JR
Actually, you only need to use new ActiveXObject("Msxml2.XMLHTTP"). Do
not worry about the current MSXML version registered in the client's
computer.
--
JR
So, returning to the OP's original code, I'd suggest the following
snippet:
Ajax = {
transport : (function () {
var req = null;
if (typeof window.XMLHttpRequest != "undefined") {
// IE7+, FF, Opera 8.0+, Safari.
try { req = new XMLHttpRequest(); }
catch (e) { } // Ignores any exception.
} else if (typeof window.ActiveXObject != "undefined") {
//IE 5+, 6, or IE7/8 with native XMLHTTP support disabled.
try { req = new ActiveXObject("Msxml2.XMLHTTP"); }
catch (ex) {
try { req = new ActiveXObject("Microsoft.XMLHTTP"); }
catch (expt) { }
}
} else {
// alert("Your browser doesn't support AJAX. Sorry!");
}
return req;
})();
// other properties.
};
--
JR
[...]
> So, returning to the OP's original code, I'd suggest the following
> snippet:
>
> Ajax = {
Don't forget var.
Oops! Thank you!
I'd also substitute:
- 'Ajax' for 'var ajax = {...}', because 'ajax' won't be used as a
constructor; and
- the semicolon (;) after the 'transport' method for just a comma (,)
because there are other properties coming after it.
Cheers,
JR
if (typeof XMLHttpRequest == "undefined") {
XMLHttpRequest = function() {
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }
catch (ignore) {}
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
catch (ignore) {}
throw new Error("This browser does not support
XMLHttpRequest.");
};
}
If you want to support browsers older than IE6, you may have to
add things like:
try { return new ActiveXObject("Msxml2.XMLHTTP"); }
catch (ignore) {}
try { return new ActiveXObject("Microsoft.XMLHTTP"); }
catch (ignore) {}
But are you sure your JavaScript will actually run perfectly in
Internet Explorer 5.x or other older browsers? Fortunately I
don't have to support those.
Hans-Georg
Firstly, the code should try new XMLHttpRequest() before the
activeXObject, because it's supported in all of the new versions of
IE, FF, Safari, Opera, and Chrome, just to mention the 'majors'.
In addition, David Mark has made a negative comment on a similar code
that I've copied (and pasted here) from the MSDN site. I think he used
the term 'awful' to describe the attempt to override the
XMLHttpRequest object.
> If you want to support browsers older than IE6, you may have to
> add things like:
>
> try { return new ActiveXObject("Msxml2.XMLHTTP"); }
> catch (ignore) {}
> try { return new ActiveXObject("Microsoft.XMLHTTP"); }
> catch (ignore) {}
>
> But are you sure your JavaScript will actually run perfectly in
> Internet Explorer 5.x or other older browsers? Fortunately I
> don't have to support those.
According to my readings on the Microsoft website (MSDN mainly), it is
not really necessary to specify the version of the Msxml2.XMLHTTP
ActiveX object. When you write 'new ActiveXObject("Msxml2.XMLHTTP");',
the browser (IE) tries to instantiate the active version registered in
the computer's OS (Windows).
Cheers,
JR
Why is it that this thread keeps missing var?
The problem with adding a user-defiend global XMLHttpRequest is that it
will trip up other code that makes the same check:-
if(typeof XMLHttpRequest == "undefined") {
>On 31 dez, 12:37, Hans-Georg Michna <hans-
>georgNoEmailPle...@michna.com> wrote:
>> Had to look into this last night and will use something like
>> this, gleaned from Wikipedia:
>>
>> if (typeof XMLHttpRequest == "undefined") {
>> � XMLHttpRequest = function() {
>> � � try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }
>> � � � catch (ignore) {}
>> � � try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
>> � � � catch (ignore) {}
>> � � throw new Error("This browser does not support
>> XMLHttpRequest.");
>> � };
>>
>> }
>Firstly, the code should try new XMLHttpRequest() before the
>activeXObject, because it's supported in all of the new versions of
>IE, FF, Safari, Opera, and Chrome, just to mention the 'majors'.
I guess you didn't read it right. The code checks whether
XMLHttpRequest is undefined and, if so, defines it. Then you can
use XMLHttpRequest as if your browser were standards-conformant.
If the browser is standards-conformant, the code does nothing,
and you use the original XMLHttpRequest.
>According to my readings on the Microsoft website (MSDN mainly), it is
>not really necessary to specify the version of the Msxml2.XMLHTTP
>ActiveX object. When you write 'new ActiveXObject("Msxml2.XMLHTTP");',
>the browser (IE) tries to instantiate the active version registered in
>the computer's OS (Windows).
This doesn't agree with what I learned.
Hey, another year is coming! Have to go and have a drink.
All the best to all the JavaScript junkies for another year of
many sucesses!
Hans-Georg
>Hans-Georg Michna wrote:
>> Had to look into this last night and will use something like
>> this, gleaned from Wikipedia:
>>
>> if (typeof XMLHttpRequest == "undefined") {
>> XMLHttpRequest = function() {
>Why is it that this thread keeps missing var?
>
>The problem with adding a user-defiend global XMLHttpRequest is that it
>will trip up other code that makes the same check:-
>
>if(typeof XMLHttpRequest == "undefined") {
>
>}
Can't see any problem with it. Another program would likely do
the very same thing. If it finds the global XMLHttpRequest
already, it has to do nothing and can happily use it.
Typing this after shooting fireworks in the air after 2010 just
began over here in Europe. (:-)
Hans-Georg
> Hans-Georg Michna wrote:
>> Had to look into this last night and will use something like
>> this, gleaned from Wikipedia:
>>
>> if (typeof XMLHttpRequest == "undefined") {
>> XMLHttpRequest = function() {
>
> Why is it that this thread keeps missing var?
>
> The problem with adding a user-defiend global XMLHttpRequest is that it
> will trip up other code that makes the same check:-
>
> if(typeof XMLHttpRequest == "undefined") {
>
> }
You are not making any sense.
PointedEars
--
Danny Goodman's books are out of date and teach practices that are
positively harmful for cross-browser scripting.
-- Richard Cornford, cljs, <cife6q$253$1$8300...@news.demon.co.uk> (2004)
> Hans-Georg Michna wrote:
>> On Thu, 31 Dec 2009 14:07:37 -0800, Garrett Smith wrote:
>>> Hans-Georg Michna wrote:
>>>> Had to look into this last night and will use something like
>>>> this, gleaned from Wikipedia:
>>>>
>>>> if (typeof XMLHttpRequest == "undefined") {
>>>> XMLHttpRequest = function() {
>>>
>>> Why is it that this thread keeps missing var?
>>>
>>> The problem with adding a user-defiend global XMLHttpRequest is that it
>>> will trip up other code that makes the same check:-
>>>
>>> if(typeof XMLHttpRequest == "undefined") {
>>>
>>> }
>
> It makes your user-defined function look like the real deal.
And that is bad[tm], because ...?
Because they are not exactly equivalent.
Making the user-defined function look like the real deal is bad because
it isn't. If a script makes a check:-
var IS_NATIVE_XHR = typeof XMLHttpRequest !== "undefined";
- the script is misled. It might seem safe, but it isn't; the
user-defined new XMLHttpRequest() can throw:-
"This browser does not support XMLHttpRequest."
Yeah, a bad example (Very bad indeed!!!) from Wikipedia:
http://en.wikipedia.org/wiki/XMLHttpRequest
It's not even necessary to check for "Msxml2.XMLHTTP.6.0",
"Msxml2.XMLHTTP.5.0", 4.0 or 3.0. Just try using "Msxml2.XMLHTTP", and
"Microsoft.XMLHTTP" as a last alternative. As I posted earlier, the
version of the MSXML Parser is dependent on the Windows version, not
on the IE version, according to the MSDN article that I cited (on that
post).
--
JR
I wrote this example to let someone know which XLMHTTP versions are
available on his/her computer:
<script type="text/javascript">
(function () {
var xmlVersions = [
'Microsoft.XMLHTTP',
'Msxml2.XMLHTTP.3.0',
'Msxml2.XMLHTTP.4.0',
'Msxml2.XMLHTTP.5.0',
'Msxml2.XMLHTTP.6.0'
], i, len, xhr, ret = "<p>";
for (i = 0, len = xmlVersions.length; i < len; i++) {
try {
xhr = new ActiveXObject(xmlVersions[i]);
if (xhr) { ret += xmlVersions[i] +"<br>"; }
} catch(ex) { }
}
document.write('MSXML versions available on this computer:<br>' +
ret + '</p>');
})();
</script>
--
JR
Yes, that seems reasonable. I don't recall ever having to deal with
the version numbers instantiating COM objects (for any component) in
desktop development (of course, it's been quite a long time). IIRC,
looking at how the entries are registered with the OS is
illuminating. So glad I never have to deal with such MS nonsense
again. :)
This is from the old CWR project (and ended up in My Library). It was
discussed (here) quite a bit back then. I don't remember the details,
but the rationale seemed reasonable to me at the time.
// for legacy eg. IE 5
function() {
return new global.ActiveXObject("Microsoft.XMLHTTP");
},
// for fully patched Win2k SP4 and up
function() {
return new global.ActiveXObject("Msxml2.XMLHTTP.3.0");
},
// IE 6 users that have updated their msxml dll files.
function() {
return new global.ActiveXObject("Msxml2.XMLHTTP.6.0");
},
> Thomas 'PointedEars' Lahn wrote:
>> Garrett Smith wrote:
>>> Hans-Georg Michna wrote:
>>>> On Thu, 31 Dec 2009 14:07:37 -0800, Garrett Smith wrote:
>>>>> The problem with adding a user-defiend global XMLHttpRequest is that
>>>>> it will trip up other code that makes the same check:-
>>>>>
>>>>> if(typeof XMLHttpRequest == "undefined") {
>>>>>
>>>>> }
>>> It makes your user-defined function look like the real deal.
>>
>> And that is bad[tm], because ...?
>
> Making the user-defined function look like the real deal is bad because
> it isn't.
Nonsense.
> If a script makes a check:-
>
> var IS_NATIVE_XHR = typeof XMLHttpRequest !== "undefined";
>
> - the script is misled. It might seem safe, but it isn't; the
> user-defined new XMLHttpRequest() can throw:-
>
> "This browser does not support XMLHttpRequest."
And that is bad[tm], because ...? If it CAN `throw' (there is no MUST),
scripts using it can (and should) `try'...`catch'.
> Thomas 'PointedEars' Lahn wrote:
>> JR wrote:
>> > Thomas 'PointedEars' Lahn wrote:
>> >> David Mark wrote:
>> >> > On Dec 23, 3:57 pm, JR <groups_j...@yahoo.com.br> wrote:
>> >> >> On Dec 23, 3:59 pm, Mychal Hackman <mycha...@gmail.com> wrote:
>> >> >> Firstly, lose that "return false" in the catch(e) { } block.
>> >> >> Secondly, you've got to try "Msxml2.XMLHTTP" too, not just
>> >> >> "Microsoft.XMLHTTP".
>> >> > As noted.
>> >> What would one need "Msxml2.XMLHTTP" for?
>> >
>> > According to Adam Wiener, Lead Program Manager for Data
>> > Programmability/XML Technologies (Microsoft), the “Microsoft”
>> > namespace is actually older and is only implemented in MSXML3 for
>> > legacy support. So “msxml2” namespace should be used for
>> > instantiating objects.
>> >
>> > http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right
>> > version-of-msxml-in-internet-explorer.aspx
>>
>> That does not answer my question, though.
>>
>> And since "Microsoft.XMLHTTP" apparently means the same as
>> "MSXML2.XMLHTTP.2.0", what he suggested in 2006 did not make sense then
>> and is not making more sense now. Replacing the identifier with
>> "MSXML2.XMLHTTP.3.0" as he suggests would break compatibility. Adding
>> the identifier would decrease efficiency for older implementations at no
>> advantage. Surely you are not suggesting Microsoft would deliberately
>> break backwards-compatibility?
>
> According to MSDN (http://msdn.microsoft.com/en-us/library/
> cc507441%28VS.85%29.aspx)
> - "MSXML 3.0 and prior versions of MSXML are version independent.
> MSXML 3.0 was the last version of MSXML to support version-independent
> CLSIDs and ProgIDs."
So what?
> - "MSXML 3.0 is supported in Windows Vista; Windows Server 2003;
> Windows XP; Windows 2000; Windows 98; Windows Millenium Edition (ME)."
So what?
> You seem to be mistaking MSXML versions with IE versions.
No, I don't. Curious how you got that idea again.
> You may look
> for msxml3.dll (v. 3.0) in your Windows\System32 folder to verify the
> above information.
This information is irrelevant to the discussed problem.
> Since the OP's original code 'try-catch' only version 2.0
> (Microsoft.XMLHTTP), I think his code needs to test for version 3.0 as
> well (MSXML2.XMLHTTP).
What for, if "Microsoft.XMLHTTP" is apparently both upwards and downwards-
compatible (running on Vista with msxml3.dll to msxml6.dll, and Windows XP
with msxml3.dll only, but no msxml2.dll or msxml1.dll here)?
And suppose the advantage is bugfixes in the greater versions, why not
prefer all the greater versions to get the latest bugfixes?
Your argument can be turned up and down, it simply does not make any sense.
No, the argument is clear for those who pay minimum attention while
reading.
JR
>And suppose the advantage is bugfixes in the greater versions, why not
>prefer all the greater versions to get the latest bugfixes?
Perhaps not all of them. Microsoft recommends not to use
versions 4 and 5. They say, check for 6.0. If that is not there,
use 3.0.
All earlier versions are for IE versions before 6.
Hans-Georg
Is that so? Then, pray tell, what exactly is the advantage of
trying "Msxml2.XMLHTTP" before or instead of "Microsoft.XMLHTTP"?
PointedEars
--
Use any version of Microsoft Frontpage to create your site.
(This won't prevent people from viewing your source, but no one
will want to steal it.)
-- from <http://www.vortex-webdesign.com/help/hidesource.htm> (404-comp.)
I've found an article on MSDN stating exactly the same. Furthermore,
there's another article on MSDN that doesn't encourage using version
5, which is installed by Office 2003. Inspecting my computer which has
Office 2003, I've confirmed that msxml5.dll is located in:
"C:\Program files\Common files\Microsoft Shared\OFFICE11\MSXML5.DLL"
> All earlier versions are for IE versions before 6.
Nowadays, it might be odd to find IE 5 or 5.5 on an XP or Vista
installation. But, in that case, I suppose IE 5 could instantiate
versions 3 and 6, because both XP and Vista come with the mentioned
versions of MSXML. But I can't test this supposition.
After searching my Windows Vista registry for "Msxml2.XMLHTTP", I've
found out two CLSID keys: one related to the "msxml2.dll" (version 2.6
on my system) and the other related to the "msxml3.dll" (version 3.0).
This is consistent with the MSDN article "MSXML 3.0 GUIDs and
ProgIDs" (http://msdn.microsoft.com/en-us/library/ms766426%28VS.
85%29.aspx).
But searching further, I've noticed that both "Microsoft.XMLHTTP" and
"Microsoft.XMLHTTP.1.0" are related to the msxml3.dll too. What? Now
it's not consistent with the same article on MSDN.
I've used VBA to instantiating "Msxml2.XMLHTTP", but I couldn't find
any property informing the dll filename or XMLHTTP version. I think
I'd need some C++ code to inspect what is really going on...
In short, after studying this matter I think the best approach would
be (referring to the OP's example):
var ajax = {
transport : (function () {
var req = null;
if (typeof window.XMLHttpRequest != "undefined") {
// IE7+, FF, Opera 8.0+, Safari.
try { req = new XMLHttpRequest(); }
catch (ex1) { } // Ignores.
} else if (typeof window.ActiveXObject != "undefined") {
//IE 5+, 6, or IE7/8 with native XMLHTTP support disabled.
try { req = new ActiveXObject("Msxml2.XMLHTTP.6.0"); }
catch (ex2) {
try { req = new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
catch (ex3) {
try { req = new ActiveXObject("Microsoft.XMLHTTP"); }
catch (ex4) { } // game over.
}
}
} else {
// alert("Your browser doesn't support AJAX. Sorry!");
}
return req;
})(),