AJAX In Action [97]
Licensed to jonathan zheng 186 CHAPTER 5 The role of the server Table 5.1 Behavior of instanceof operator across frames Object Created In instanceof Invoked In Obj instanceof Object Evaluates To Top-level document Top-level document true Top-level document IFrame false IFrame Top-level document false IFrame IFrame true Importing the same object definition into multiple scripting contexts is not as simple as it first looks. We can avoid it by providing a factory method as part of our top-level document’s API, for example: function createPlanetInfo(name,description){ return new PlanetInfo(name,description); } which our script can then call without needing to refer to its own version of the PlanetInfo type, thus: The showPopup() function in listing 5.4 is essentially a factory for the ScriptIframePopup object. This approach works and does what we want it to. We need to send a small amount of HTML boilerplate with each page, but much less than with the contentcentric solution. The biggest drawback of this approach appears to be the creation of a separate JavaScript context. There is a way to avoid that altogether, which we will look at now. Loading scripts using XMLHttpRequest and eval() JavaScript, like many scripting languages, has an eval() function, which allows any arbitrary text to be passed directly to the JavaScript interpreter. Using eval() is often discouraged, or noted as being slow, and this is indeed the case when it is called regularly on lots of small scripts. However, it has its uses, and we can exploit it here to evaluate scripts loaded from the server using the XMLHttpRequest object. eval() performs with reasonable efficiency when working on fewer, larger scripts. Licensed to jonathan zheng The details: exchanging data 187 Our planetary info example is rewritten to use eval() in the following code: function showInfo(event){ var planet=this.id; var scriptUrl="script_"+planet+".js"; new net.ContentLoader(scriptUrl,evalScript); } function evalScript(){ var script=this.req.responseText; eval(script); } The showInfo() method now uses the XMLHttpRequest object (wrapped in our ContentLoader class) to fetch the script from the server, without needing to wrap it in an HTML page. The second function, evalScript(), is passed to the ContentLoader as a callback, at which point we can read the responseText property from the XMLHttpRequest object. The entire script is evaluated in the current page context, rather than in a separate context within an IFrame. We can add the term script-centric to our pattern language now and make a note that there are two implementations of it, using IFrames and eval(). Let’s step back then, and see how script-based approaches compare with the contentbased style. Problems and limitations When we load a script directly from the server, we are generally transmitting a simpler message, reducing bandwidth to some extent. We also decouple the logic from the presentation to a great degree, with the immediate practical consequence that visual changes aren’t confined to a fixed rectangular portion of the screen as they are with the content-centric approach. On the downside, however, we introduce a tight coupling between client and server code. The JavaScript emitted by the server is unlikely to be reusable in other contexts and will need to be specifically written for the Ajax client. Further, once published, the API provided by the client will be relatively difficult to change. It’s a step in the right direction, though. The Ajax application is starting to behave more like an application and less like a document. In the next style of client-server communication that we cover, we can release the tight coupling between client and server that was introduced here. Licensed to jonathan zheng 188 CHAPTER 5 The role