jQuery vs MooTools

jQuery vs MooTools

Most people getting started with JavaScript these days are faced with the challenging task of picking a library to use, or at least which one to learn first. If you're working for a company chances are they have already chosen a framework for you, in which case the point is somewhat moot. If this is the case and they've chosen MooTools and you're used to jQuery, then this article might still be of some use to you.

Every day on twitter I see numerous posts that boil down to "MooTools or jQuery?" This article aims to help you make that choice.

Disclaimer

I am a MooTools developer. I work on the MooTools framework. I blog about MooTools. I wrote the main online tutorial and the book about MooTools. Obviously, I have a perspective that is somewhat biased. I'll also point out that I don't use jQuery that often. If you're a jQuery developer and see anything that I have misrepresented here, please contact me and help me rectify the issue. My objective here is to be helpful and accurate to people - not to sell one framework over another.

Purpose

Helping you make a choice between these two frameworks involves me telling you how they are different. I'll start out by saying thatboth of them are excellent choices. You can't make a wrong choice here. Both frameworks have their strengths and weaknesses, but, in general, they are both great choices. There are other frameworks out there that are worth digging into as well. DojoPrototype,YUIExt and others are all great choices. Which one you choose really has more to do with your own style and what you need to accomplish. The purpose of this article is to focus on MooTools and jQuery, as increasingly these are the two frameworks that I see a lot of people considering. Finally, I'm not trying to convince anyone to switch from one framework to the other. There are interesting things about both frameworks from which you can learn. You can read a little more about this article and why I wrote it in my blog post on Clientcide where I announced it.

Table o' Contents

The Stats

  jQuery Core MooTools Core
Library Size 55.9K 64.3K
Features
License MIT & GPL MIT
DOM Utilites yes yes
Animation yes yes
Event Handling yes yes
CSS3 Selectors yes (a subset) yes (a subset)
Ajax yes yes
Native Extensions (excluding Element) about a dozen for Array, Object, and String about six dozen for Array, Object, String, Function, and Number
Inheritance Not supported directly with jQuery Provided with Class constructor
Other Considerations
plug-ins Hundreds of unofficial plug-ins in a directory at plugins.jquery.com Roughly 4 dozen official plug-ins available at mootools.net/more. Unofficial plugin directory at mootools.net/plugins.
Official UI library yes no

Information based on data from jquery.commootools.net, and wikipedia.com.

The Mottos Say It All

If you go to the jQuery site, here's what it says at the top of the page:

jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. jQuery is designed to change the way that you write JavaScript.

...and if you go to MooTools, this is what you'll find:

MooTools is a compact, modular, Object-Oriented JavaScript framework designed for the intermediate to advanced JavaScript developer. It allows you to write powerful, flexible, and cross-browser code with its elegant, well documented, and coherent API.

I think this really sums it up. If you ask me (and you're reading this, so I'll assume you just have), the question isn't about which framework is better or worse. It's which one of these things above do you want to do? These two frameworks just aren't trying to do the same things. They overlap in the functionality they provide, but they are not trying to do the same things.

jQuery's description of itself talks about HTML, events, animations, Ajax, and web development. MooTools talks about object oriented-ness and writing powerful and flexible code. jQuery aspires to "change the way you write JavaScript" while MooTools is designed for the intermediate to advanced JavaScript developer.

Part of this consideration is the notion of a framework vs a toolkit. MooTools is a framework that attempts to implement JavaScriptas it should be (according to MooTools' authors). The aim is to implement an API that feels like JavaScript and enhances everything; not just the DOM. jQuery is a toolkit that gives you an easy to use collection of methods in a self-contained system designed to make the DOM itself more pleasant. It just so happens that the DOM is where most people focus their effort when writing JavaScript, so in many cases, jQuery is all you need.

Most of the code you write when you write MooTools still feels like JavaScript. If you aren't interested in JavaScript as a language, then learning MooTools is going to feel like a chore. If you are interested in JavaScript and what makes it interesting, powerful, and expressive, then, personally, I think MooTools is the better choice.

The Learning Curve and The Community

First, jQuery is, by and large, easier to learn. It has an almost colloquial style that almost doesn't feel like programming. If all you want is to get something working quickly without learning JavaScript, jQuery is probably a better choice for you. It's not that MooTools can't help you accomplish the same things, but I'll admit that MooTools can be a little harder to get the hang of if you're new to JavaScript and also that there are just a lot of resources out there to help you learn jQuery - more than there are for MooTools at least.

If you compare the jQuery community (see the "Discussion" page on jQuery) and the MooTools community (ircmailing list, andunofficial forum) you'll quickly discover two things: 1) the jQuery community is far larger (I attribute this mostly to the point I made above about how easy it is to learn, but also because...) and 2) they are more active in promoting the library. If you measure jQuery and MooTools on metrics like the number of people using it, the number of search queries run on Google, the number of books sold, etc, you'll see jQuery is ahead by a wide margin.

To tell you why you might consider MooTools I'll first need to talk a little bit about what both the frameworks do. Ultimately which framework you choose is going to come down to what you want to accomplish and how you like to program (and maybe even if you like to program, at least in JavaScript).

What JavaScript Is Good For

Part of making this choice is asking what you want to do with JavaScript. Let's consider vanilla JavaScript. No framework; just plain old JS. JavaScript gives you native objects like StringsNumbersFunctionsArraysDatesRegular Expressions, and more. JavaScript also gives you an inheritance model - a somewhat esoteric model called prototypal inheritance (which I'll talk about more later). These building blocks and the concept of inheritance are the bread and butter of any programming language and they have absolutely nothing to do with browsers or the web or CSS or HTML. You could write anything you wanted to in JavaScript. Tic-tac-toe, chess, photoediting, a web server, whatever. It just so happens that 99% of all the JavaScript out there is run in browsers and that's what we think of it as. The programming language for browsers.

Understanding that the browser, the DOM, is just where we happen to use JS most of the time but that it's actually a very robust and expressive programming language will help you understand the difference between MooTools and jQuery.

More Than Just The DOM

If you think of the tasks that we want to accomplish in JavaScript strictly in terms of "get stuff on the page and do stuff to it" then jQuery is probably the best choice. It excels at offering a very expressive system for describing behavior on the page in a way that doesn't feel like programming sometimes. You can still use the rest of JavaScript to do what you want to do, but if you're focused on the DOM - changing CSS properties, animating things, fetching content via AJAX, etc - most of what you'll end up writing will be covered by jQuery, and what isn't will likely be plain-old JavaScript. jQuery does provide some methods that aren't about the DOM; for example, it provides a mechanism for iterating over arrays - $.each(array, fn) - or, for example, it offers a trim method for strings -$.trim(str). But there aren't a ton of these types of utility methods, which is fine because, for the most part, if you're just getting stuff out of the DOM, iterating over them, and altering them in some way (adding html, changing styles, adding event listeners for click and mouseover, etc) you don't need much else.

But if you think of JavaScript's scope in its full breadth, you can see that jQuery doesn't focus on things outside of the DOM. This is one of the reasons it is so easy to learn, but it also limits the ways it can help you write JavaScript. It's just not trying to be anything other than a solid programming system for the DOM. It doesn't address inheritance nor does it address the basic utilities of all the native types in the JavaScript language, but it doesn't need to. If you want to mess around with strings, dates, regular expressions, arrays and functions, you can. It's just not jQuery's job to help you do it. JavaScript as a language is there at your feet. jQuery makes the DOM your playground, but the rest of JavaScript is just not in its scope.

This is where MooTools is vastly different. Rather than focusing exclusively on the DOM (though, as I'll get into in a bit, it offers all the functionality that jQuery does but accomplishes this in a very different manner), MooTools takes into its scope the entire language. If jQuery makes the DOM your playground, MooTools aims to make JavaScript your playground, and this is one of the reasons why it's harder to learn.

Inheritance with JavaScript

The JavaScript programming language has some really awesome things about it. For starters, it's a functional language, which means that it treats functions as high-order objects that can be passed around as variables just like any other object - strings or numbers for example. It's designed with this concept in mind and many of the methods and patterns in it work best when you write code this way. It's the difference between:

for (var i = 0; i < myArray.length; i++) { /* do stuff */ }

and

myArray.forEach(function(item, index) { /* do stuff */ });

JavaScript has an inheritance model that is not quite unique but at least rather rare in programming languages. Instead of classes that are defined that can be subclassed it passes along traits through prototypal inheritance. This means that objects inherit directly from other objects. If you reference a property on an object that inherits from another object, the language inspects the child object for that property and, if it doesn't find it, looks for it on the parent. This is how a method on, say, an array works. When you type:

[1,2,3].forEach(function(item) { alert(item) }); //this alerts 1 then 2 then 3

the method "forEach" is not a property of the array you declare ([1,2,3]), it is a property of the prototype for all Arrays. When you reference this method the language looks for a method called forEach on your array, and, not finding it, then looks at the prototype for all arrays. This means that the forEach method is not in memory once for every array in memory; it is only in memory for the prototype of arrays. This is incredibly efficient and ultimately quite powerful. (Side note: MooTools aliases the forEach method aseach)

Self Reference

Javascript has a special word: "this". It's hard for me to succinctly define what "this" is all about but, by default, "this" is the object to which the current method belongs. It allows objects to refer to themselves within their methods as they would otherwise have no means to do so. This becomes important when you create children objects and have numerous instances of that object; how else could the method of an object refer to itself? When the actual copy of the method exists on the parent, not the child, the "this" keyword allows these instances to refer to their own state. (here's a much more complete description of the "this" keyword, and another from Mozilla.)

The "this" keyword allows objects that inherit from other objects to refer to themselves, but there are times when you may want to reference something else through "this". This is called binding, wherein you specify a different "this" for a method. The "each" method on Array allows you to specify the bound object with a second argument. Here's an example of where you might want to pass in a different "this":

var ninja = {
    weapons: ['katana', 'throwing stars', 'exploding palm technique'],
    log: function(message) {
        console.log(message);
    },
    logInventory: function() {
        this.weapons.each(function(weapon) {
      //we want "this" to point to ninja...
            this.log('this ninja can kill with its ' + weapon);
        }, this); //so we pass "this" (which is ninja) to Array.each  
    }
};
ninja.logInventory(); 
//this ninja can kill with its katana
//this ninja can kill with its throwing stars
//this ninja can kill with its exploding palm technique

In the example above, we bind ninja (which is "this" inside the logInventory method) to the method we pass to the array so that we can refer to the log property of ninja. If we didn't do this, "this" would be window.

These are just some examples of the power and expressiveness that JavaScript has to offer - inheritance, self reference and binding, and efficient prototype properties. The bad news is that vanilla JavaScript doesn't make these powerful things very useful or accessible, and this is where MooTools starts. It makes these types of patterns easy and rather pleasant to use. You end up writing more abstract code, and in the long run, this is a good thing - a powerful thing. Learning how these patterns are valuable and how to use them properly takes effort, but the up side is that the code you author is both highly reusable and much easier to maintain. I'll talk about these two things a bit more in a minute.

MooTools Makes JavaScript Itself More Fun

Because MooTools focuses on making the JavaScript API itself more stable and coherent, it is focused less on giving you an interface that "changes the way you write JavaScript" and more on making JavaScript as a whole less frustrating; MooTools is an extension to the JavaScript language. MooTools tries to make JavaScript the way it is meant to be. A significant portion of the core library is spent on augmenting Function, String, Array, Number, Element and other prototypes. The other big thing it offers is a function called Class.

Now, Class looks to many people like it's trying to recreate a more classical inheritance model that one might find in Java or C++, but that's not the case. What Class does is make the prototypal inheritance model of JavaScript easier for you and me to access and take advantage of. I'll note that these concepts are not unique to MooTools (other frameworks offer similar functionality), but both of these concepts are not present in jQuery. jQuery does not offer an inheritance system nor does it offer any enhancements to native objects (Function, String, etc). This is not a deficiency of jQuery as the authors of jQuery could easily offer these things. Rather, they have designed a toolkit with a different goal in mind. Where MooTools aims to make JavaScript more fun, jQuery aims to make the DOM more fun and its designers have chosen to limit their scope to that task.

jQuery Makes the DOM More Fun

And this is why jQuery is more accessible. It doesn't ask that you learn JavaScript inside and out. It doesn't throw you into the deep end with prototypal inheritance, binding, "this", and native prototypes. When you get started with jQuery in its official tutorial, this is the first jQuery code example you find:

window.onload = function() {
    alert("welcome");
}

and here's the third:

$(document).ready(function() {
    $("a").click(function(event) {
        alert("Thanks for visiting!");
    });
});

If you read the MooTools book or the MooTools tutorial (both of which I authored) they start in a much different place. While you can skip ahead and quickly learn about effects and the DOM, if you want to learn MooTools, you have to start with things like Class, and, I'll admit: if you're new to programming, or you just want to get something working on your site without having to learn everything about JavaScript, chances are jQuery is going to look a lot more friendly to you.

On the other hand, if you want to learn JavaScript itself, MooTools is a great way to do it. It implements a lot of things that JavaScript is going to have (many of the methods on Natives are just the JavaScript 1.8 spec and beyond). If you're used to programming, especially both object oriented and functional programming, MooTools has a lot of design patterns that are very exciting and expressive.

Anything You Can Do I Can Do Better

If you look at the things jQuery can do, there's often a counterpart in MooTools. If you look at the things MooTools can do, there is often no way to emulate it using jQuery code because of jQuery's focus on the DOM. MooTools has a broader functionality than jQuery, but there's nothing about jQuery that prevents you from doing those things. For example, jQuery does not come with any sort of inheritance system, but that's ok. You could, if you want, use the MooTools Class in conjunction with jQuery if you wanted to (or write your own). There's even an inheritance plug-in for jQuery (I haven't used it, but I assume it offers pretty much the same kind of functionality).

If we look at the example from jQuery above:

$(document).ready(function() {
    $("a").click(function(event) {
        alert("Thanks for visiting!");
    });
});

and we wanted to translate this to MooTools, we'd have:

window.addEvent('domready', function() {
    $$('a').addEvent('click', function(event) {
        alert('Thanks for visiting!');
    });
});

These are very similar no?

Here's a more complex example from jQuery:

$(document).ready(function() {
    $("#orderedlist li:last").hover(function() {
        $(this).addClass("green");
    },
    function() {
        $(this).removeClass("green");
    });
});

and in MooTools:

window.addEvent('domready',function() {
    $$('#orderedlist li:last-child').addEvents({
        mouseenter: function() {
            this.addClass('green');
        },
        mouseleave: function() {
            this.removeClass('green');
        }
    });
});

Again, very similar. I'd argue that the MooTools version is more explicit, but also more verbose because of it. It's clear reading the MooTools code that we're adding two events - one for mouse enter and one on mouse leave, while the jQuery version is more concise; its hover method accepts two methods - the first for mouse enter and the second for mouse leave. I personally like the fact that the MooTools code is more legible but that's a very subjective observation.

I will say that sometimes jQuery can become too esoteric for my taste. The methods don't always make sense to me just looking at them and I find it hard to parse. This is somewhat unfair though, as I am intimately familiar with MooTools, so reading MooTools is easy for me. But one of the things I appreciate about MooTools is how almost all the method and class names really name the thing. Methods are almost always verbs and leave little doubt as to what they do. Every programming language requires you to go to the docs to look up syntax when you write it - I'm not saying that. I'm just saying that I find the API of MooTools to be more coherent and consistent.

MooTools Lets You Have It Your Way

But what if you like the jQuery syntax? One way to illustrate the power of MooTools is to show you how easy it is to change it to suit your tastes. If we wanted to implement the hover method from jQuery in MooTools, we could easily do so:

Element.implement({
    hover : function(enter,leave){
       return this.addEvents({ mouseenter : enter, mouseleave : leave });
    }
});

//and then you could use it exactly like the jQuery version:
$$('#orderlist li:last').hover(function(){
   this.addClass('green');
},
function(){
   this.removeClass('green');
});

Indeed, there are MooTools plug-ins that do just that; give you the jQuery syntax for MooTools. MooTools' focus on extensibility means you can implement anything you like. This is something jQuery can't do. MooTools can mimic jQuery if you want it to, but jQuery can't mimic MooTools. If you want to write classes or extend native prototypes or do some of the other things MooTools can, you'll have to write it yourself.

Chaining as a Design Pattern

Let's do another of these. Here's some jQuery (from the jQuery tutorial):

$(document).ready(function() {
    $('#faq').find('dd').hide().end().find('dt').click(function() {
        $(this).next().slideToggle();
    });
});

This is an example of a syntax that I personally don't prefer. Looking at the code above I am hard pressed to be sure of what it's doing. Most notably I'm curious about what .end does and how does .find, which follows it, relate to what .end does? Now, looking at the docs on jQuery makes it very clear what .end does (it resets to the value of the original selector, in this case #faq). But this seems very odd to me. When I do work with jQuery, I often find myself unsure what a method is going to return to me. Obviously this doesn't bother everyone else as jQuery has a lot of people happily using it, so I'll chalk it up to personal preference again.

Let's look at the above logic as MooTools:

window.addEvent('domready', function() {
    var faq = $('faq');
    faq.getElements('dd').hide();
    faq.getElements('dt').addEvent('click', function() {
        this.getNext().slide('toggle');
    });
});

Again, the MooTools code is a bit more verbose, but also more explicit. Also note that the design pattern here is to store the reference to #faq in a variable, where jQuery uses its .end method to return to it. I'll note that it's possible to write highly chained code with Mootools. For example:

item.getElements('input[type=checkbox]')
  .filter(function(box) {
    return box.checked != checked;
  })
  .set('checked', checked)
  .getParent()[(checked) ? 'addClass' : 'removeClass']('checked')
  .fireEvent((checked) ? 'check' : 'uncheck');

But really, writing code like this - lots of logic in a domready statement - with either framework, I'd argue, is itself a bad practice. It's far better to encapsulate your logic into reusable chunks.

Reusing Code with jQuery

It's very tempting when you're working on a web project to write code this way. Just add some logic on the page that selects the DOM elements and "sets them up" by hiding some, altering others, and adding event listeners for click or mouseover. Developing code this way is very efficient, very fast. The problem with writing all your logic in domready statements is that you end up with a lot of code that does the same thing in different places. If we take the FAQ pattern above we could easily apply the same logic elsewhere on a different page with any list of terms and definitions. Are we going to repeat the same logic every time we find this pattern?

A simple way to make it reusable is to wrap the logic in a function and pass in arguments. Here's what that might look like in jQuery:

function faq(container, terms, definitions) {
    $(container).find(terms).hide().end().find(definitions).click(function() {
        $(this).next().slideToggle();
    });
};
$(document).ready(function() {
    faq('#faq', 'dd', 'dt');
});

This is much better for two really big and important reasons:

  1. If tomorrow we need to change how these lists work (maybe we want to add click tracking logic so we can measure it in our web logs or maybe we want to fetch the definitions via ajax) we can just change our main faq method and everywhere we use it just gets updated. Or if there's a new version of jQuery released that changes the way things work, we can just go update our one method instead of a dozen copies everywhere. I call this keeping a small footprint in my application. By keeping the points where my application touches my more generic code as small as possible, it makes it easier for me to fix bugs, upgrade frameworks, add features, or alter functionality.
  2. The second reason is that it's less code. By reusing the same method over and over again, I don't repeat myself and this is valuable in any programming environment. It also makes the code my visitors have to download smaller.

jQuery actually has a slightly more refined system for writing reusable "widgets" like these. Rather than encourage you to drop them into functions like the above example (which is really rather crude) it encourages you to write jQuery plug-ins. Here's what that would look like:

jQuery.fn.faq = function(options) {
    var settings = jQuery.extend({
        terms: 'dt',
        definitions: 'dd'
    }, options); 
  //"this" is the current context; in this case, the elements we want to turn into faq layouts
    $(this).find(settings.terms).hide().end().find(settings.definitions).click(function() {
        $(this).next().slideToggle();
    });
    return this;
};

which you would use thusly:

$('#faq').faq();

But looking at the example above, there's not much difference between declaring our faq function this way vs. declaring it as a stand alone function. Granted, it's not in the global namespace, but we could have just as easily added it to a namespace of our own. By attaching it to jQuery we can chain it with other jquery methods. The other benefit is that the "this" inside our function is the current context of whatever is in the jQuery chain at that moment. By using this pattern for plug-ins we're able to make our plug-in look like it's part of jQuery, but other than that, our plug-in is basically a single function that takes the current jQuery context, does stuff to it, and then returns the context for the next item in the chain. There's not a lot of complexity here, which makes it easy for anyone to write jQuery plug-ins - they're just single functions.

Note that it is possible to write more complex plugins with jQuery with methods and state. This kind of pattern is supported with the jQuery UI plugin system and doesn't use the same mechanism as the basic plugin (like our faq example). Instead, you attach an object with methods and properties to the jQuery object (i.e. $.ui.tabs). There's a shortcut to invoke this object ($(selector).tabs()) so that you can continue chaining as with the faq plugin. But because it doesn't return a reference to the tabs object created for the items in your selector, you're forced to call that selector again to invoke methods on it. Instead of calling myTabInstance.add(url, label, index)you must execute the selector again and call your function by name (as a string): $(selector).tabs('add', url, label, index);. This means you're running your selector twice (unless you store it in a variable somewhere), and that you don't ever have a pointer to the "add" method that you can do things like bind or delay. This post is focused on the MooTools and jQuery cores, and while jQuery's UI system does provide this functionality, it's not something that comes with jQuery by default.

Reusing Code with MooTools

In MooTools when you want to define a pattern, you're more likely to use either a Class or implement a method into a native object (into String, for example).

Rather than give you an almost completely different language from JavaScript's native style, MooTools attempts to walk the middle ground between defining its own custom syntax and extending JavaScript's own design patterns. One of the ways it does this is by extending the prototypes of the native objects in the language and in the DOM. This means that if you needed a method to trim a string, MooTools encourages you to add that method to String itself (note that String.trim is already in MooTools; you don't need to add this yourself):

String.implement({
    trim: function() {
        return this.replace(/^\s+|\s+$/g, '');
    }
});

This means you can just execute " no more spaces on the end! ".trim() and get back "no more spaces on the end!". Some would say that implementing properties into native prototypes is inappropriate. It's the reason why MooTools and Prototype.js can't play well with each other - any framework that manipulates prototypes of natives doesn't play well with any other framework that does the same. If I define String.prototype.foo() and another library on the same page defines it, too, which ever one comes last wins. In a way, this is similar to the problem we face with the global window namespace. This is how JavaScript works. This is how JavaScript 1.8 has added so many features. It adds them to the prototypes.

The MooTools developers include a robust framework that is easy for you to extend with your own functionality with the intention that people who include the framework in the page are going to use it, not some other framework. It's actually kind of rude to ask users to download two frameworks. The only reason to include two frameworks is because you want to use plug-ins from both, and in the minds of the MooTools authors (myself included), if you want a plug-in that isn't available with the framework of your choice, it's more appropriate for you to spend the time porting it to your environment than to ask your users to download another framework.

Once you learn how JavaScript works and see the power of extending native objects, a whole new level of programming opens up. You can write plug-ins that alter Elements or Dates or Functions. While some might argue that adding methods to natives this way is a kind of pollution, I'd argue that this is how JavaScript is meant to be used. It is a design feature of the language. By attaching methods to natives you allow your code to be concise and compartmentalized. jQuery does this too, but limits its prototype enhancements to the jQuery object.

While you can easily chain multiple method calls on the jQuery object, on any other type of object you have to use generics. For example, in jQuery if you want to trim a string and then iterate over each line, you would have to write:

$.each( $.trim( $('span.something').html() ).split("\n"), function(i, line){alert(line);});

But because MooTools modifies prototypes, you can do this:

$('span.something').get('html').trim().split("\n").each(function(line){alert(line);});

Taking a look at this makes it extremely clear how powerful it is to modify prototypes. Chaining on DOM elements isn't the only place chaining is useful. MooTools lets you chain methods on any object, including running a method on multiple elements at once.

The key here is that at the heart of the MooTools framework is the notion that it's there to let you program what you want. If there's functionality that's not in the core, you can extend it and add your own. The job of the core is not to provide everyone with every bit of functionality that they could ever want, but to provide the tools that allow you write the things that you want. A big part of that is making it easier to extend the prototypes of natives, and take advantage of prototypal inheritance. You can do these things with vanilla JavaScript but MooTools makes it easier and more pleasant.

MooTools and Inheritance

Despite its name, the MooTools Class function is not really a class nor does it create them. It has design patterns that might remind you of classes in a more traditional programming language, but really Class is all about objects and prototypal inheritance. (Unfortunately, using words like "class" are the most convenient way to describe these things, so for the purposes of this article, when I refer to "classes" I'm referring to functions that return objects - which I'll call "instances" - that inherit from a prototype.)

To make a class, you pass an object to the Class constructor like this:

var Human = new Class({
    initialize: function(name, age) {
        this.name = name;
        this.age = age;
    },
    isAlive: true,
    energy: 1,
    eat: function() {
        this.energy = this.energy + 1; //same as this.energy++
    }
});

You pass Class an object (above, we pass an object with members like "isAlive" and "eat") and this object becomes the prototype of every instance of that class. To create an instance, you call it like this:

var bob = new Human("bob", 20); //bob's name is "bob" and he's 20 years old.

Now we have an instance of Humanbob has the properties of the object we defined when we created our Human class. But the important thing is that bob has these properties through inheritance. When we reference bob.eatbob doesn't really have this property. JavaScript looks at bob and he doesn't have an eat method, so it looks up the inheritance chain and finds it on the object we passed when we created the Human class. This is true for energy, too. At first glance this looks potentially bad; we don't want all the humans we create to gain energy every time that bob eats. The important thing to recognize is that the first time we assign a value tobob's energy, we assign him his own value and we no longer look at the prototype for it. So the first time bob eats, he gets his own definition for energy (set to 2).

bob.eat(); //bob.energy == 2

Note that bob's name and age are unique to him; these are assigned to him when the class is initialized in the initialize method.

This whole pattern may seem a little odd to you, but the value here is that we can define functionality for a pattern and create instances of that pattern every time we need it. Each instance maintains its own state. So if we create another instance each one is independent of the other, but inherits from the same base pattern:

var Alice = new Human();
//alice.energy == 1
//bob.energy == 2

Where things get really interesting is when we want to augment this behavior.

Extending and Implementing Classes

Let's revisit our jQuery faq plug-in. What would happen if we wanted to add more functionality to that plug-in. What if we wanted to make an ajax version that fetched the answers to the questions from the server? Let's imagine that the faq plug-in was authored by someone else and we want to add more to it without altering it in any way (we don't want to fork it).

Our only real choices are to either duplicate the faq plug-in's logic entirely (remember, it's a single function), essentially forking it, or we can invoke it and then add more logic to it. Given a choice, the latter seems to save us the most trouble. It would look something like this:

jQuery.fn.ajaxFaq = function(options) {
    var settings = jQuery.extend({ 
    //some ajax specific options like the url to request terms from
        url: '/getfaq.php'
        definitions: 'dd'
    }, options); 
  //"this" is the current context; in this case, the elements we want to turn into faq layouts
    $(this).find(settings.definitions).click(function() {
        $(this).load(.....); //the logic to load the content from the term
    });
    this.faq(); //call our original faq plug-in
});

This has some down sides. First of all, our faq class is going to repeat our selector for the definitions, which might be expensive; there's no way to store the retrieved definitions and pass it on for the second time they are needed. Secondly, we can't add our ajax logic into the middle of the faq plug-in's own logic for displaying the definition. The original plug-in called slideToggle which expands the definition using an effect. This is problematic because this effect is going to go off before our ajax finishes loading. There's no real solution here unless we just duplicate the entire faq plug-in.

Now let's consider our MooTools Human class. It has properties like isAlive and energy and it has a method called eat. What if we wanted to make a new version of Human that had additional properties? With MooTools, we extend the class:

var Ninja = new Class({
    Extends: Human,
    initialize: function(name, age, side) {
        this.side = side;
        this.parent(name, age);
    },
    energy: 100,
    attack: function(target) {
        this.energy = this.energy - 5;
        target.isAlive = false;
    }
});

You can see that we've added a lot of functionality here into a subclass. This subclass has all these properties that are unique to Ninjas.Ninjas start off with an initial energy value of 100. Ninjas get a side. They also get an attack method that lets them kill other Humans, but it costs the Ninja energy.

var bob = new Human('Bob', 25);
var blackNinja = new Ninja('Nin Tendo', 'unknown', 'evil');
//blackNinja.isAlive = true
//blackNinja.name = 'Nin Tendo'
blackNinja.attack(bob);
//bob never had a chance

Picking this apart a bit, there are some interesting things to consider here. Note that we have an initialize method in the Ninja class. This would appear to overwrite the initialize method in the Human class, but we can still access it by calling this.parent, passing along the arguments that the parent class's initialize expects. Further, we can control when our logic occurs; before or after the call to the parent. We can assign new values to properties (like the energy value) and we can define new functionality. Imagine if we could do this with our faq plug-in for jQuery. We could load our ajax and THEN slide open the value.

MooTools has another pattern called a Mixin. Unlike the parent to child relationship that is defined by extending one class into a subclass, you can also define classes that are mixed into other classes to imbue them with their properties. Here's an example:

var Warrior = new Class({
    energy: 100,
    kills: 0,
    attack: function(target) {
        target.isAlive = false;
        this.energy = this.energy - 5;
        this.kills++;
    }
});

Here we've broken the qualities that make a Ninja different from a Human and put them in a class of their own. This lets us reuse this code outside of Ninja. We could then imbue our Ninja class with the qualities of a warrior like so:

var Ninja = new Class({
    Extends: Human,
    Implements: Warrior, //can be an array if you want to implement more than one
    initialize: function(name, age, side) {
        this.side = side;
        this.parent(name, age);
    }
});

Ninja still works as it did before, but Warrior is at our disposal to reuse:

var Samurai = new Class({
  Extends: Human,
  Implements: Warrior,
  side: 'good'
});

Now we have a Samurai class and a Ninja class. But look at how little code both Ninja and Samurai took to define. Both of them are similar in that they are humans with warrior qualities, but they are different in that samurais are always, always good, while ninjas have shifting allegiances. By spending the time to write a Human class and a Warrior class, we're able to have three different classes with no repetition of code while maintaining a very granular level of control over when methods are called and how they relate to each other. Each instance we create has its own state and the code itself is very legible.

Now that you have an overview of how classes work in MooTools, let's look at our faq class that we wrote in jQuery and write it as we would in MooTools and then extend it to add Ajax to it just as we did with jQuery.

var FAQ = new Class({
  //Options is another class provided by MooTools
  Implements: Options,
  //these are the default options
  options: {
    terms: 'dt',
    definitions: 'dd'
  },
  initialize: function(container, options) {
    //we store a reference to our container
    this.container = $(container);
    //setOptions is a method provided by the Options mixin
    //it merges the options passed in with the defaults
    this.setOptions(options);
    //we store the terms and definitions
    this.terms = this.container.getElements(this.options.terms);
    this.definitions = this.container.getElements(this.options.definitions);
    //we call our attach method
    //by breaking this into its own method
    //it makes our class easier to extend
    this.attach();
  },
  attach: function(){
    //loop through the terms
    this.terms.each(function(term, index) {
      //add a click event to each one
      term.addEvent('click', function(){
        //that calls our toggle method for
        //the current index
        this.toggle(index);
      }, this);
    }, this);
  },
  toggle: function(index){
    //toggle open the definition for the given index
    this.definitions[index].slide('toggle');
  }
});

Woah. That's a lot of code. Even if we remove all the comments it's still two dozen lines long. I already illustrated above that we could build this plug-in with roughly the same amount of code as the jQuery version. So why is this one so much longer? Well, we've made it much more flexible. To use the class, we just call the constructor, like this:

var myFAQ = new FAQ(myContainer);
//and now we can call methods on it if we want:
myFAQ.toggle(2); //toggle the 3rd element

We can access methods and properties of the instance. But what about our ajax functionality? The problem with our ajax extension to the jQuery version was that we couldn't delay the opening of the definition until after it loaded. We don't have that problem with our MooTools version:

FAQ.Ajax = new Class({
  //this class inherits the properties of FAQ
  Extends: FAQ,
  //it also gets a new option in addition to the other defaults
  //this one for url, that we're going to append the index of the
  //term to; in reality we might make this more robust, but for
  //this example it serves the purpose
  options: {
    url: null;
  },
  //we're going to cache the results, so if a section is opened
  //twice, we won't hit the server for the data
  indexesLoaded: [],
  toggle: function(index){
    //if we've already loaded the definition
    if (this.indexesLoaded[index]) {
      //just call the previous version of toggle
      this.parent(index);
    } else {
      //otherwise, request the data from the server
      new Request.HTML({
        update: this.definitions[index],
        url: this.options.url + index,
        //and when the data is loaded, expand the definition
        onComplete: function(){
          this.indexesLoaded[index] = true;
          this.definitions[index].slide('toggle');
        }.bind(this)
      }).send();
    }
  }
});

Now we have a version of our FAQ class that allows us to get the definitions from the server. Note that we were able to integrate the new logic in a way that doesn't expand the definition until after the content comes back from the server (which we couldn't do with the jQuery version). Also note that we really only had to describe the new functionality (the ajax) and little else. This extensibility makes it possible for you to create families of plug-ins that offer different shades of functionality. It also means that you can use someone else's plug-in and alter just the bits that you to be want different if you need to (without forking it). This helps explain why, for any given design pattern - a date picker, a tab interface, etc, that you typically only find a few plug-ins for MooTools. Most of the plug-ins you get either solve your problem or, if not, you can just extend them to add the things you need.

As I illustrated earlier, it's possible to write complex jQuery widgets with methods and state. Most of the code you write when doing this is vanilla JavaScript when you need to express logic that isn't related to the DOM. But jQuery's model doesn't offer a system for extending these instances into subclasses. Nor does it help you with mixins that can be reused easily. Finally, jQuery's plugins are always attached to DOM elements. If you wanted to write a class that, say, processed URLs, there's no stateful system for such a thing unless you write it yourself.

Decision Time

jQuery focuses on expressiveness, quick and easy coding, and the DOM while MooTools focuses on extension, inheritance, legibility, reuse, and maintainability. If you put those two things on opposite sides of a scale, the jQuery side translates into something with which it's easy to get started and see quick results but (in my experience) can turn into code that's harder to reuse and maintain (but really that's up to you; it's not jQuery's problem, per se), while the MooTools side takes longer to learn and requires you to write more code upfront before you see results, but afterwards is more reusable and more maintainable.

Further, the MooTools core does not contain every feature you can imagine and neither does the jQuery core. Both frameworks keep their cores rather lean, leaving it to you and others to write plug-ins and extensions. Their job is not to give you every feature you could want but to give you the tools so that you can implement anything you can imagine. This is the power of JavaScript, and of JavaScript frameworks in general, and both frameworks excel at it. MooTools takes a more holistic approach and gives you tools to write anything you can imagine beyond the scope of the DOM, but pays the price by having a steeper learning curve. MooTools' extensible and holistic approach gives you a superset of jQuery's features, but jQuery's focus on a slick DOM API doesn't preclude you from using the native inheritance methods of JavaScript or from using a class system like MooTools if you want it.

This is why I say that both frameworks are excellent choices. My effort here has been to highlight the differences in philosophies between the two codebases and highlight their advantages and disadvantages. I doubt I've been successful in keeping my preference for MooTools completely in check, but hopefully this has been helpful. Regardless of which framework you choose to work with, you now know a lot more about both, hopefully. If you have the luxury of time, I strongly recommend implementing a site with each. Then write your own review of them both and maybe your perspective will highlight some things I missed.

A history of this document can be viewed on github.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值