Monday, June 11, 2012

Injecting a private jQuery, Part 4: AMD Asynchronous Module Definition

Ok...  What is AMD?  Let me start by saying I just learned about this yesterday, and just barely started understanding it about 2 hours ago.  So I am NO expert on this subject.  I can tell you that AMD (which in my mind originally stood for Advance Micro Devices - that Intel competitor) will not start to mean Asynchronous Module Definition.

I'll take a stab at the meaning now...  AMD means defining your module in such a way that it can be safely loaded (along with its dependencies) asynchronously - without exposing itself globally and clobbering the global namespace (namely window.?).

Again, if you look at jQuery (which is the focus of this series) and peer into the jQuery source, at its very start, it is an anonymous function call that starts the ball rolling.  That anonymous function call then creates the private variable jQuery which is the library of jQuery functions.  At the end of the anonymous function, the private variable is then exposed to the browser by attaching it to BOTH window.jQuery and window.$ (when referencing these variables in your JavaScript code, you will typically not reference the "window." part and just call jQuery and/or $).  But what if there were a way to gain access to that variable WITHOUT attaching it to the global "window." namespace?  That is the essence of AMD and a new family of products known as "AMD Loaders".

An AMD Loader basically instantiates an AMD compliant module and passes the reference of that module to a function - never involving the global namespace.  Making a module AMD compliant is really nothing more than calling a special function on the AMD Loader called "define" which gives the module a "name" and returns the module as an object.  In fact, you may not know this, but jQuery v1.7 is actually an AMD compliant module.  Again, peering into the source code, we find this:

// Expose jQuery to the global object
window.jQuery = window.$ = jQuery;

// Expose jQuery as an AMD module, but only for AMD loaders that
// understand the issues with loading multiple versions of jQuery
// in a page that all might call define(). The loader will indicate
// they have special allowances for multiple jQuery versions by
// specifying define.amd.jQuery = true. Register as a named module,
// since jQuery can be concatenated with other files that may use define,
// but not use a proper concatenation script that understands anonymous
// AMD modules. A named AMD is safest and most robust way to register.
// Lowercase jquery is used because AMD module names are derived from
// file names, and jQuery is normally delivered in a lowercase file name.
// Do this after creating the global so that if an AMD module wants to call
// noConflict to hide this version of jQuery, it will work.
if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
 define( "jquery", [], function () { return jQuery; } );
}

Right after the private jQuery variable is hooked into the global namespace (now starting to be "bad practice"), the source checks for the existence of the "define" function and, if found (along with some other conditions), will call the define function with "jquery" as the common "module name" and returning the private jQuery variable.

OK...  so this is a little hypocritical, right?  I mean, where did the "define" method/function come from?  It must be in the Global Namespace, right?  Well...  Ok, I guess we have to start somewhere, so I'll let that one go.  In fact, if you read some of the documentation on the AMD loaders websites, you can see that they hope for the browsers to adopt a new JavaScript that will have an actual definition for JavaScript Modules so that the global namespace is not needed.  Here is a good initial explanation:
http://unscriptable.com/2011/03/30/curl-js-yet-another-amd-loader/

So where are the AMD loaders at this time?  There are several right now:
http://requirejs.org/
https://github.com/unscriptable/curl
http://bdframework.org/bdLoad/

Now, these are NOT to be confused with asynchronous "loaders".  The "loadJS" function I wrote in Part 3 of this series was an example of an asynchronous loader.  There are more sophisticated loader libraries out there, one of the most prevalent is LABjs:
http://labjs.com/

The AMD loader that I have been researching is RequireJS.  I am not going to try and do a tutorial on RequireJS at this point because I have not done anything practical with it up to this point.  However, I will write a little of what I have learned and how it pertains to this series of articles.

Here are a couple of points to consider:

1) Although jQuery v1.7 is now "AMD Compliant", as we saw above, it is still adding to the global namespace so that it remains backwards compatible.

2) RequireJS has some "special jQuery" coding to recognize if you load an earlier version of jQuery (pre-1.7).  Although I could not find this "special code" as described by this blog post (but I encourage the reading of the blog anyway):
http://www.bennadel.com/blog/2287-Using-jQuery-As-A-Named-Module-In-RequireJS.htm

3) The RequireJS library has a lot of documentation on including jQuery with RequireJS.  The most prevalent one uses a combined requirejs-jquery.js file which is jQuery combined with the RequiredJD library.  I would avoid this structure.  There is a good beginner blog about combining RequireJS with jQuery here:
http://www.bennadel.com/blog/2275-Using-RequireJS-For-Asynchronous-Script-Loading-And-JavaScript-Dependency-Management.htm

4) RequireJS includes a "optimizer" utility for combining multiple scripts together and minify-ing them.  At this point I am ignoring that part of RequireJS.

5) In today's jQuery plugin world, plugins are not actually "modules".  They simply latch on to the global jQuery namespace object and attach themselves there.  This is obviously not very "AMD Compliant".  So RequireJS has many different ways to handle this (order! & use plugins are often sited), but the current "flavor" for handling jQuery plugin dependencies is using RequireJS 2.0 and it's new "shims" configuration.

Here are some other references on jQuery, AMD and RequireJS:

https://github.com/amdjs/amdjs-api/wiki/jQuery-and-AMD
Wiki page on jQuery and AMD.

http://bugs.jquery.com/ticket/10545
This is a bug report/request for jQuery to stop attaching itself to the global namespace and a discussion by the developer of RequireJS as a response.  A few of the links in this article are also interesting reading.

http://addyosmani.com/writing-modular-js/
An excellent article on the proposed new version of JavaScript (ES Harmony) and the current state of AMD.

http://blog.errorception.com/2012/01/writing-quality-third-party-js-part-1.html
Excellent post on writing 3rd-Party JavaScript widgets.

http://alexmarandon.com/articles/web_widget_jquery/
Excellent practical implementation of jQuery in a widget situation.  However, it does not show how to load jQuery plugins that your widget code may depend on.  The author suggests that he simply includes the minified plugin code WITH his script.  That is, once jQuery is loaded, he does NOT load the plugin, but rather includes the plugin minified code in the onload of jQuery so that it executes right there.

http://www.angrycoding.com/2011/09/managing-dependencies-with-requirejs.html
More dependency management for jQuery and RequireJS.

No comments:

Post a Comment