MooTools Types 1.3

By Ryan Florence, published 2010-11-10

Part of the issue MooTools 1.3.

MooTools has a concept of Types (previously called Natives in < 1.3). Generally, they are the same as the Types in the ECMAScript Spec, except it adds on a few others.

ECMAScript 5 has added some great new stuff to some of these, unfortunately many browsers are outdated but still in use. MooTools upgrades these environments to support many of the new, plus dozens of additional, methods. This upgrade to and enhancement of the native types sets a cross-environment foundation for the rest of MooTools itself and your application.

// stuff in ECMAScript 5
"   this whitespace is outta control  ".trim();
[1,2,3].forEach(function(item){ console.log(item) });
var doubled = [1,2,3].map(function(x){ return x * 2 });
Object.keys({ one: 1, two: 2, three: 3 }); // => ['one', 'two', 'three'];

// this stuff isn't in the spec, but is awesome
var myArray = [null, 1, 0, true, false, 'foo', undefined, ''];
myArray.clean() // => [1, 0, true, false, 'foo', '']

"transition:fade".test(/^transition/); // => true

var arr = ['IMG_1111', 'IMG_2222'];
arr.include('IMG_1111'); // already exists, does nothing
arr.include('IMG_3333'); // includes it
arr.erase('IMG_1111'); // arr => ['IMG_2222', 'IMG_3333']
[1,1,2,3].unique(); // => [1,2,3] (from MooTools More)

var obj = {one: 1, two: 2, three: 3};
Object.values(obj); // => [1,2,3]
Object.filter(obj, function(i){ return i < 3 }); // => {one: 1, two: 2}

It’s awesome because it’s just like JavaScript

Typically, JavaScripters will hang some of these kinds of functions from a namespace. Let’s play around with an array using Dojo, jQuery, and MooTools, side-by-side. Let’s pretend like we run a dating site and need to figure out who is datable for our 20-year-old user.

Note: A great way to understand something is to contrast it with the alternatives. This article is in no way attempting to start a framework war. Rather, it is attempting to show the reader the way MooTools handles things s/he may have been doing already in other libraries, with commentary on why I prefer it. My preference to MooTools should not be misinterpreted to a criticism of the others.

var ages        = [20, 25, 41, 32],
    halfedPlus7 = function (item){ return item / 2 + 7 },
    lessThan20  = function (item){ return item < 20 };

// dojo one liner
var datable = dojo.filter(dojo.map(arr, halfedPlus7), lessThan20).sort();

// jQuery two liner, a bit more readable, but an extra line and useless variable
var mapped  = jQuery.map(arr, halfedPlus7),
    datable = jQuery.filter(mapped, halfedPlus7).sort();

// MooTools
var datable = arr.map(halfedPlus7).filter(lessThan20).sort();

A few points I’d like to make, MooTools

Also note that this example uses 100% ECMAScript 5 code but makes it work in every browser. If the browser supports ES5 natively, MooTools leaves them alone and the native code is used.

Yeah, but it’s polluting the Array prototype! - Some Angry Guy

That’s a valid concern. If your code will be shipped off to an unknown environment that you don’t control, I emphatically suggest you don’t use MooTools (or any library, actually.) Lucky for MooTools, 99% of the time a person sits down to write JavaScript that’s not the case. MooTools #1 priority is the API, and these prototype extensions are the keystone to it. After all, it’s a framework; it expects you to have the freedom to extend JavaScript.

Borrowing methods with generics

A common pattern is to simply borrow methods from other objects. MooTools creates generic functions for the prototype methods of a Type. Here’s a common line of code: first without the help from MooTools, and second with a generic. Once again, MooTools produces less code that’s easier to scan through and understand.

function go(){
   Array.prototype.slice.call(arguments, 1); // => [2,3]
   Array.slice(arguments, 1); // => [2,3]
}

go(1,2,3);

You can extend it too

Nearly every time MooTools extends JavaScript, it first creates an API to accomplish the task, allowing you to extend it the very same way. Let’s revisit the dating web site example with Dojo and jQuery.

But first, indulge me while I get on my code organization soap box. In the aforementioned example we wrote some logic in the middle of our application code. Everybody knows you should separate business logic from implementation, but few people do so with their JavaScript. MooTools makes it easy by providing several APIs to put business logic where it most appropriately belongs.

In this example, we can remove the logic from our application code, and put it on the appropriate Types. Each type has an implement method, which is the same method MooTools uses internally.

// in some external file containing all of your Type extensions for the app

Number.implement({

  toYoungestDatableAge: function(){
    return (this / 2 + 7);
  }

});

Array.implement({

  datable: function(age){
    return this.filter(function(item){
      return item.toYoungestDatableAge() < age;
    });
  }

});

// in your application code
var ages  = [20, 25, 41, 32],
    datable = ages.datable(20).sort();

Let’s talk about this a bit more.

No logic in the application code

Application code should simply create some objects and describe how they interact with the rest of the application. All of the logic should be tucked away elsewhere, into manageable, testable chunks.

Testing logic inside your application code is impossible

There’s no way to test the age calculations from the first example outside of the application. You down wit’ TDD? Yeah, you know me.

Type extensions are ridiculously easy to test and write specs for

Let’s write some simple tests:

console.log(
  (34).toYoungestDatableAge() == 24 ? 'passed' : 'failed',
  ' => number.toYoungestDatableAge'
);

console.log(
  [20, 25, 41, 32].datable(20).join(', ') == '20, 25' ? 'passed' : 'failed',
  ' => array.datable'
);

// gives us:
'passed  => number.toYoungestDatableAge'
'passed  => array.datable'

One last thought. When extending the native types, usually you want to return the same type of object, like an array for array methods. But sometimes it makes sense to return something else–i.e. "100px".toInt() returns a number.

How this compares to other approaches

MooTools is not the only one with places to put things, here are some examples from what you’ll wee outside of MooTools, with my usual commentary that follows.

$.youngestDatableAge(n, function(){ /* logic */ })
$.datable(ages, age, function(){ /* logic */ });

dojo.youngestDatableAge(n, function(){ /* logic */ })
dojo.datable(agesArray, age, function(){ /* logic */ });

function youngestDatableAge(n){ /* logic */ };
function datable(a, x){ /* logic */ };

It’s not always clear what the arguments should be just by looking at the code

This is especially the case if the author names things vaguely, making it even more critical to write comments and documentation. You’ll note in the MooTools version, you don’t hardly need any comments. Because they are methods of Number and Array, you already know where to use them, rather than having to know what type of argument ages should be.

Additionally, you don’t normally have to type check that this is indeed an array. The combination of the Type being extended and a descriptive method name makes things pretty clear at a glance.

Functions are placed on already crowded namespaces

How many more Array, Number, Function, and String methods can we hang from a namespace before we need another namespace? By implementing new prototype methods in the appropriate places there’s more room for everybody.

We can even have the same method name for different Types, like array.double() and number.double(). On a namespace you’d need NS.double(arr) and NS.doubleNumber(n), and then you should probably go back and rename the first to NS.doubleArray–or maybe reorganize and do NS.utils.number.double and NS.utils.array.double and then do a find and replace on your whole code base.

Do I think namespaces are a bad thing? Absolutely not, they are a completely valid pattern. But when you can use MooTools as the framework for your application, you can organize your code more semantically and write shorter, cleaner code.

[1,2,3].method().method2();
NS.utils.number.method2(NS.utils.array.method([1,2,3]));

Continue on to the next article in this issue: MooTools Class 1.3.

Go back to the last article in this issue: Three-Minute MooTools

Hi, I'm Ryan!

Location:
South Jordan, UT
Github:
rpflorence
Twitter:
ryanflorence
Freenode:
rpflo

About Me

I'm a front-end web developer from Salt Lake City, Utah and have been creating websites since the early 90's. I like making awesome user experiences and leaving behind maintainable code. I'm active in the JavaScript community writing plugins, contributing to popular JavaScript libraries, speaking at conferences & meet-ups, and writing about it on the web. I work as the JavaScript guy at Instructure.