You may have already heard this, or maybe not. But this won’t change the fact that javascript is changing and the way people write it and specifically, how they write the libraries, modules and widgets we all share and use is slowly being edged towards an AMD pattern. This stands for Asynchronous Module Definition, pioneered by the DOJO framework with a one main goal in mind: being able to define and load any module and its dependencies asynchronously. Whereas it is not a standard yet, it is considered as a step towards the module system proposed in ES Harmony. I also won’t go into any possible shortcomings of this dependency system and the dangers of remote dependencies (bloat), that’s an entirely different topic.
Anyway, with that out of the way, what is the single most important thing to take away from the message that MooTools 2.0 (aka, Milk) will be AMD compliant? In order to become truly modular and compartmentalised into the small parts that have been the building blocks of the framework for years, MooTools is going to stop changing and extending the javascript natives Types’ prototypes. Only then does it guarantee that any of its modules can co-exist with any other AMD module without any conflicts.
When I say ‘stop changing’ natives, it does not mean it won’t ever modify an existing method (Function.bind comes to mind as an example) but in all likelihood, it won’t be doing much of that. You can start to treat bits of it like a microjs library. Even mix-n-match your favourite components from around the web, eg, take Class and use it on top of jQuery (if you really have to!), or bring Sizzle into MooTools instead of Slick with the utility functions of Underscore.js.
Sounds like a win. Now, the unpleasant part. Not being able to modify Element.prototype (for example) means a likely end to code like this:
document.id("someid").addEvent("click", function(e) { this.addClass("open"); var divsWithImages = this.getElements("li").filter(function(el) { return !!el.getElement('img'); }); // I know you can use a reverse combinator selector for this. not the point. });
Though the team will probably do a function wrapper like jquery that can allow the above in part, document.id() will stop returning the element itself but will return a function instead. The modified code may look the same but how it’s interpreted will be different. For instance, this.addClass(“open”); won’t work at all. Instead, it will probably need to go through either $(this) / document.id(this) or via the native Type or Slick.
Basically, all Array, Element, String etc methods (see the popular MooTools Types) are still available on the native object instead. You _could_ write the above like so:
Element.addClass($(this), 'open');
The Array / Elements.filter can be called like this:
var divsWithImages = Array.filter(document.id(this).getElements("li"), function(el) } return !!document.id(this).getElement("img"); });
Although I honestly have no idea what the new API will really look like, one thing is certain: even as of MooTools version 1.4.3 (time of writing), you can dual use most methods on the host object or through the prototype.
eg,
var strTest = "dimitar was here"; // old way via String.prototype console.log(strTest.capitalize()); // Dimitar Was Here // new way, same result. console.log(String.capitalize(strTest));
It’s a small shift in the way we write code. Since both work already, I would advise you to use the latter one so you have less of a refactor to do if you ever hope to drive your existing code base with the future versions of mootools.
Now, how do you create a String method that will work with both the prototype and the host object? Seems simple. The scope is the host object so, here is a sample rot13 method that can work as both…
(function() { var n; String.implement({ rot13: function() { return String(this).replace(/[a-z]/gi, function(match) { return String.fromCharCode((n = match.charCodeAt() + 13) > 122 || n < 104 && n > 90 ? n - 26 : n); }); } }); })(); console.log(strTest.rot13()); console.log(String.rot13(strTest));
I suspect Type.prototype will become protected in the sense that implement will not affect them, making implement behave like Object.extend does at present. If you swapped .implement for .extend in the above code block and then String(this) to String(namedArg), you get a simple and safe-ish native type method.
This is all too similar to how you extend the Object Type (prototyping Object itself has always been a bad idea as everything in javascript inherits from Object):
// extend, not implement. Object.extend({ getPropertiesCount: function(object) { var props = [], hasOwnProperty = this.prototype.hasOwnProperty; for (var prop in object) { if (hasOwnProperty.call(object, prop)) props.push(prop); } return props.length; } }); console.log(Object.getPropertiesCount(this)); // return your global variables count!
I am certain these will not be the only changes but they will certainly be the most difficult to get used to. On the upside, there will be a compatibility layer as usual, including the ability to ‘install’ the prototype methods where possible. Even so, don’t be naive and write code that won’t outlive mootools 1.x without a lot of changes.
In conclusion, things are looking up for MooTools – it is headed in the right direction, even if slowly. My only gripe with this is that we need somebody from the core team to give news of how the release of Milk is coming along every so often. So we can get ready or even help…
*note: parts of this post are based on hearsay and sporadic data from various mootools members over IRC and Twitter. Don’t go writing any books on MooTools 2.0 just yet, you may even have to change the title (cough-Aaron-cough)*
GitHub flavoured markdown enabled.