Cixar

XML/RSS

Categories:

/ (105)
  art/ (4)
    tale/ (1)
  bookmark/ (2)
  langlubber/ (4)
  movies/ (2)
  music/ (1)
    garageband/ (2)
  photo/ (1)
  politics/ (1)
  program/ (27)
    cli/ (1)
    javascript/ (12)
      chiron/ (5)
    swil/ (2)
    tale/ (22)
  reading/ (4)
  tale/ (24)
  writing/ (2)

Archives:

2008-Aug
2008-May
2008-Apr
2008-Mar
2008-Feb
2008-Jan
2007-Jun
2007-May
2007-Apr
2007-Mar
2007-Feb
2007-Jan
2006-Oct
2006-Sep
2006-Aug
2006-Jun
2006-May
2006-Apr
2006-Mar
2006-Feb
2006-Jan
2005-Dec
2005-Nov
2005-Oct
2005-Sep
2005-Aug
2005-Jul
2005-Jun
2005-May
2005-Apr
2005-Mar


The Sourcerer

by Kris Kowal.

Tue, 27 Feb 2007

Javascript Modules Revisited

For those of us who retch at the mention of both "Java" and "Javascript", today's post brings us deeper into my realm of insanity, "deep in the shit", to quote Colbert. Yes, it's no picnic, but the development world has its human moments. Moments where hubris, playful cynicism, and violent indignation must give way to humility. People, harken to my words, hidden among the copious piles of refuse, there's some good code in Dojo.

Don't run off and commit resources to it; I'm just saying it's a good read for someone looking for snippets to steal. If you need something that works today, it might be a good option, but hold your horses; I haven't finished reading the alternatives.

I've mentioned that Dojo has a module system. It plainly doesn't meet our needs. Let's be explicit about our requirements

Module system must:

  • be the first and only Javascript file explicitly loaded with a script tag in the header of a document
  • have a light footprint on the global context object (window in browsers), ideally one function
  • isolate each module in its own closure
  • isolate each module with its own context object, preferably the module object
  • leave no conditional module management code to users
  • provide singleton module objects that are loaded once per page
  • provide modules as shallow objects, encouraging the library user to obey the "Law of Demeter"
  • handle synchronous and asynchronous pattern interface
  • permit the library user to require a module under any pseudonym it desires, or vaguely, provide the flexibility of Python's import syntax, with suitable analogs for import module, import module as m, from module import *, from module import part as p.
  • permit the library user to move an entire directory of scripts without breaking any of its references to other modules within the same directory tree nor break references to global modules

Dojo does not appear to follow all of these edicts. For example, all modules use the global object, window, as their context object. Every top level module, dojo for example, leaves a footprint on the global object. Dojo encourages Demeter violations like, dojo.component.subcomponent.WhatYouReallyWant. The list of annoyances goes on.

However, Dojo does load modules synchronously, ironically with the so called "Asynchronous XML HTTP Request". My solution in Cixar Javascript, modules load asynchronously using DOM or write tricks. Modules register themselves when they're text loads. Register isolates the context object and closure.

Neither my solution nor Dojo's solution address the problem of mobile libraries. This is a critical feature for a real module system, since it would permit authors across the web to isolate a tool in a directory and permit users to place and name that directory anywhere and any way they want without having to modify the contained code.

The problem with my system was that it requires modules have to register their location:

this.register('module.js', function (module) {

This was necessary because of the non-blocking, asynchronous module loader. There was no way for a Javascript to recognize what module it was supposed to be since the module scripts would load in an indeterminate order.

Enter Dojo's module loader code. This bit of AJAX is pretty good, with lots and lots of embedded knowledge of multiple Javascript environments and their eccentricities. Using it as a reference, I wrote a new module loader system that provides both synchronous and asynchronous forms for require, and obviates the need for register calls completely.

A module:

/* import module */
var module = require('module.js');

/* import module, asynchronously */
require('module.js', function (module) {
});

/* import module as m */
var m = require('module.js');

/* from module import * */
with (require('module.js')) {
}

/* from module import component */
var component = require('module.js').component;

/* from module import component as c */
var c = require("module.js').component;

Most of all, the register function no longer exists. Modules have their own closure and singleton context object. Modules have their own require function so (eventually) they will be able to load modules relative to their own URL rather than the URL of the containing page. Here's what it looks like:

(function (url) {
var require = function (subUrl) {...};
eval(http.requestText(url));
}).call(modules[url], url);

Meanwhile, I also re-imagined Dojo's HTTP request mechanism, the heart of AJAX. Since the module system requires HTTP requests, it creates a pseudo-module that users can later require as http.js. The HTTP module supports the same abbreviated syntax for both synchronous and asynchronous requests as require, and provides forms for retrieving the HTTP response, text, XML, and the XML document element object. The module includes most of Dojo's code paths for multiple browsers, environments, and platforms and provides a wrapper for the HTTP response object that cleaves more strictly to an observable style and handles some odd cross browser cases.

var text = http.requestText('text.txt');

http.requestXML('xml.xml', function (xml) {
});

var response = http.request('location.html');
if (response.isOk()) {
var text = response.getText();
}
this entry was posted on Tue, 27 Feb 2007 at 00:30 in program/javascript