![]() |
|
Extending Built-In Object With prototype Property (JavaScript) |
bamccaig
Member #7,536
July 2006
![]() |
I've encapsulated the XMLHttpRequest/XMLDOM objects into an Ajax wrapper "class" to make requests simpler. It's working pretty well, but there's a major flaw in the design. For those of you that don't want to read the novel I'll spoil the ending: I either need to add a parent property to the XMLHttpRequest object in IE browsers or I need the XMLHttpRequest object's onreadystatechange callback to refer to the wrapper instance with the this keyword (i.e. I need access to the wrapper instance from the onreadystatechange callback). Early on I realized that the XMLHttpRequest::onreadystatechange callback isn't aware of the parent object. That is to say, MyAjaxObject::mobjXMLHttpRequest::onreadystatechange has no way to access MyAjaxObject. The callback is essentially used to set properties of the MyAjaxObject instance, as well as trigger "events" during and after the transfer. I was seemingly able to add a parent property to the XMLHttpRequest object using the prototype property: // Extend XMLHttpRequest object with a parent property. XMLHttpRequest.prototype.parent = null; This seemed to work okay for a while, though I don't think it was actually browser safe because of the weird way XMLHttpRequest is created in IE versions. In any case, it seems that it was suddenly no longer valid and I was getting fatal errors so I resorted to a temporary global pointer declared at the top of the script: // Temporary MyAjaxObject reference. gobjMyAjaxObject = null; Before a request is made this global pointer is checked and if not yet set it's set to the current instance and a request is made. After the MyAjaxObject instance's properties are set the global pointer is reset to null. If the global pointer is already set (not null) when a request is made it's assumed that a request is in progress and the new request is aborted, signaling failure. The flaw enforces that only one request can be made at a time, which almost nullifies the point of AJAX. I need a way to add a parent property to the XMLHttpRequest object so that when created as a property of MyAjaxObject it will have a reference to it's parent object (the MyAjaxObject instance). Does anybody know how to add a property, for example, parent, to the XMLHttpRequest object (that would hopefully work in IE 5.5+ (or below if possible)?
-- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Matthew Leverton
Supreme Loser
January 1999
![]() |
You cannot with IE because it's an ActiveX object, not a native JS object. I'm not quite sure what you are getting at. This is what I do with my RPC wrapper class: var rpc = RPC.create(); rpc.responseType = RPC.JSON; rpc.foo = "bar"; rpc.onLoad = function(json) { alert(this.foo); }; rpc.get('/foo.html'); That would display "bar" in a dialog box. |
bamccaig
Member #7,536
July 2006
![]() |
Matthew Leverton said: I'm not quite sure what you are getting at. As we know, the XMLHttpRequest::onreadystatechange callback executes whenever the XMLHttpRequest::readyState changes. When the XMLHttpRequest::readyState == 4 (i.e. Complete) the MyAjaxObject::XMLDOM property needs to be set, however, XMLHttpRequest::onreadystatechange doesn't have access to it's MyAjaxObject instance's properties.. objMyAjaxObject = new MyAjaxObject(); // MyAjaxObject::Request(string url, string method, bool async); objMyAjaxObject.Request("somefile.xml", httpMethod.GET, true); /* * Because the request was asynchronous any following code will * continue to execute while somefile.xml is loaded in the * background... The actually processing of XML needs to happen * elsewhere. Elsewhere is the XMLHttpRequest::onreadystatechange callback, * in which the "this" keyword refers to the XMLHttpRequest object and not the * MyAjaxObject instance. */
I'm assuming RPC::onLoad is triggered when the XMLHttpRequest::readyState == 4? How do you bind RPC::onLoad to the XMLHttpRequest object...? Perhaps you could share RPC with me so I can learn from your superior design. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Matthew Leverton
Supreme Loser
January 1999
![]() |
Quote:
I'm assuming RPC:: onLoad is triggered when the XMLHttpRequest::readyState == 4? How do you bind RPC:: onLoad to the XMLHttpRequest object...? Right. But RPC is a wrapper class. For instance: // inside the RPC.transport constructor // 'this' is the RPC instance this.obj = new XMLHttpRequest(); if (this.obj) { this.obj.onreadystatechange = Delegate.create(this, 'onReadyStateChange'); } (FYI: For IE, I define the XMLHttpRequest function to return the ActiveX control.) You're right that you lose the "this" pointer in the onReadyStateChange function if you just do an anonymous function. That's why I use a Delegate to keep track of things. I created this method myself; there may be some other defacto standard way of doing this. Later on: RPC.transport.prototype.onReadyStateChange = function() { // this.obj points to the XMLHttpRequest object. // eventually: else if (this.responseType == RPC.JSON) { eval("var json = " + this.obj.responseText); this.onLoad(json); } } My Delegate class looks like:
|
bamccaig
Member #7,536
July 2006
![]() |
Wow, some syntax I've never seen before... Cool. Today a coworker and I (it didn't really require both of us function createXMLHttpRequest(parent) { // ... // Set the parent property. if(objXMLHttpRequest != null) objXMLHttpRequest.parent = parent; // Return the object. return objXMLHttpRequest; // ... } Unfortunately, IE6 is complaining that the parent property isn't implemented. // Default callback. this.mobjXMLHttpRequest.onreadystatechange = this.onreadystatechange_callback;
Perhaps you can explain the difference between what Delegates.create() returns and a function pointer as assigned in the above code...? It looks like you're creating a function and storing it in an array, instance._delegates[]. So I'm guessing that when you call these functions you actually call the Delegates.get() method which uses the instance pointer and _delegates index (ptr, func) to access the function instance... -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Thomas Fjellstrom
Member #476
June 2000
![]() |
Personally, why do it all yourself, when someone else has done it? It supports IE, Firefox, and works well in Konqueror as well. -- |
Matthew Leverton
Supreme Loser
January 1999
![]() |
I was going to recommend prototype.js too. It is well written and useful. I just don't use it because I hate other people's code. Anyway, I'll post back again when I have some time to give a deeper explanation of what my Delegate class does. |
Thomas Fjellstrom
Member #476
June 2000
![]() |
I really like prototype. It might be a little bloat, but if you look into all the api provides, MUCH of it is very useful in a dynamic site. I've even made widget classs based on the prototype Object/Class stuff, and its worked out very well. -- |
Matthew Leverton
Supreme Loser
January 1999
![]() |
The Delegate class I provided above is complete. Here is a simple example (not using it):
When button one is pressed, you'll see that the reference of the Foo object is lost. But when button two is pressed, the reference is maintained by an anonymous function, which I call the "delegate." All my Delegate class does is make it easy to wrap all of those inline anonymous functions into one delegate pointer. (That is, maybe you have multiple events that all want to call the same method in an object.) Now what you should be doing is creating a wrapper around your XMLHttpRequest object, similar to my object Foo. Then your onreadystatechange, would look like:
Obviously you'll need to fill in the blanks, but that is the general idea of how to build a wrapper class around XMLHttpRequest. |
ImLeftFooted
Member #3,935
October 2003
![]() |
onreadystatechange is a very ugly way to handle this situation. Much cleaner to do it this way: var obj = new MyAjaxObject(); obj.open("POST", url, false); obj.send(); if(obj.status != 200) { alert("Server connection unavailable"); return; } ...
|
Thomas Fjellstrom
Member #476
June 2000
![]() |
Quote: onreadystatechange is a very ugly way to handle this situation. Much cleaner to do it this way: And is that async? -- |
ImLeftFooted
Member #3,935
October 2003
![]() |
Right, async is what makes it so ugly. You want to avoid async. |
Thomas Fjellstrom
Member #476
June 2000
![]() |
Uh, the point to XMLHTTP is to be async. I don't want a js script hanging the browser while its downloading something. -- |
ImLeftFooted
Member #3,935
October 2003
![]() |
Who said it was going to hang the browser? |
Matthew Leverton
Supreme Loser
January 1999
![]() |
Quote: You want to avoid async. That's absolutely funny. You call it a MyAjaxObject(). Let's remind ourselves what the acronym Ajax stands for: Asynchronous JavaScript and XML. If you don't do it asynchronously, the JS interpreter will pause and hang until it's finished. Whether or not the browser remains your friend, depends on the implementation. So your suggestion is, quite frankly, terrible. |
bamccaig
Member #7,536
July 2006
![]() |
Thanks a lot, Matthew! The following appears to be working!
I'd appreciate confirmation that I'm doing things correctly... Thanks also, Thomas Fjellstrom, for the link to Prototype library or whatever... -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
ImLeftFooted
Member #3,935
October 2003
![]() |
Quote: If you don't do it asynchronously, the JS interpreter will pause and hang until it's finished. Whether or not the browser remains your friend, depends on the implementation. So your suggestion is, quite frankly, terrible. Hm, I ran some tests and it looks like the JS interpreter does pause. Thats good to know then. Looks like the _self system is probably the best answer. I try to avoid doing that because of the IE memory leak bug, but it looks like thats the only portable way to get it done. |
bamccaig
Member #7,536
July 2006
![]() |
Dustin Dettmer said: I try to avoid doing that because of the IE memory leak bug,...
Care to elaborate? -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
ImLeftFooted
Member #3,935
October 2003
![]() |
I forget the specifics but I believe in this scenario: function foo() { var i; function bar() { } bar(); } The varaible 'i' is never cleaned up. |
bamccaig
Member #7,536
July 2006
![]() |
In my example, would _self contain the entirely of the class or merely a memory address as a C++ pointer does? In other words, if this bug applies is a lot of data not being cleaned up or only a little bit? For example:
My AJAX wrapper is still working even after the delete statement. Do you think that could take care of the leak? -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Matthew Leverton
Supreme Loser
January 1999
![]() |
Quote:
I'd appreciate confirmation that I'm doing things correctly... It looks fine. Basically my RPC class looks like: var rpc = RPC.create(); rpc.addField("foo", "bar"); rpc.setResponseType(RPC.JSON); // .HTML or .XML rpc.onLoad = function(json) { alert(json.someProp); } rpc.get(url); // or .post(); The implementation is straightforward. When adding a field, I use encodeURIComponent() on the key and value like: data += encodeURIComponent(key) + "=" + encodeURIComponent(value); That goes in the get() / post() methods. (or Request in your case.) The onLoad call back is only called on success. Otherwise onError is called. (Note they are both user defined functions.) If the responseType is JSON, then it automatically gets eval()'d before calling the onLoad(). For XML, the XML doc is passed. For HTML, it's the response string. |
bamccaig
Member #7,536
July 2006
![]() |
Matthew Leverton said: rpc.addField("foo", "bar");
I never thought to encapsulate the content management as well. I simply had (;)) a property for content which was null by default. Now I have Append(). Matthew Leverton said: Otherwise onError is called.
I'm curious how you know an error occurred. Is that fired when XMLHttpRequest.status != 200? On a side note, perhaps you can shed some light on something for me. During testing I was having some trouble figuring out why certain things weren't happening... Eventually I traced it back to XMLHttpRequest.status == 0... -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
ImLeftFooted
Member #3,935
October 2003
![]() |
Quote: would _self contain the entirely of the class or merely a memory address as a C++ pointer does? My understanding is that it is a pointer. You can probably call delete manually. delete is compatible back to Javascript 1.2 apparently. I have idea where IE is on delete however... Quote: In other words, if this bug applies is a lot of data not being cleaned up or only a little bit? The whole class instance and anything it references (expired nodes, temporary functions, scopes from function calls). Javascript is particularly memory inefficient. |
|