In this second installment of the Up the Moo Herd: MooTools Tips and Tricks series, we’re going to tackle Natives, a topic included in the list of lesser-known things about MooTools.
Before anything else, I’d like to apologize for the delay in posting this article. I’ve been a little busy with a MooTools related project (which we will be releasing for public consumption soon), so I wasn’t able to finish of the rest of the articles. But the good news is that the next parts of the series are already done (whew!), and they’ll be posted by Friday.
I’d also like to thank Valerio Proietti, the lead developer of MooTools, for taking some time to answer some of my questions on this topic as well as for the idea of the Table native (which will be available in Moo2).
With that said, let’s explore the Native flora and fauna as we climb up the herd…
Going Native
In the current versions of MooTools-Core, the first constructor function you’ll encounter is a curious one called Native. Curious because it’s not documented anywhere, even though it’s one of the most important functions in the library.
To put it simply, the Native constructor simplifies the process of extending built-in native types by adding several useful methods. It actually does three things:
- It adds an
implementmethod that enables you to easily and safely add methods to existing native types. - It adds members to the native datatypes that makes it easier to deduce the datatype via
$typeandConstructor.Type - It automates the process of adding generics.
There’s really not much to say about the Native constructor, primarily because it’s non-public API and secondly because most people will rarely use it. MooTools already processes most of the native datatypes and what the core-library implements is enough for most people.
Of course, MooTools also allows us to implement other natives aside from wrapping the built-in ones. Let’s implement a trivial native called Table, which is essentially a Hash, but it keeps the information in a storage member within the object instead on the object itself.
var Table = new Native({
name: 'Table',
initialize: function(){
this.storage = {};
},
afterImplement: function(property, value){
console.log(property, value);
}
});
Table.implement({
get: function(key, fallback){
return this.storage[key] || fallback;
},
set: function(key, value){
this.storage[key] = value;
return this;
},
erase: function(key){
if (this.storage[key]) delete this.storage[key];
return this;
},
forEach: function(fn, bind){
for (var key in this.storage) {
fn.apply(bind, [this.storage[key], key, this]);
}
},
empty: function(key){
Table.each(this, function(k, v){
delete this.storage[key]
}, this);
return this;
}
});
Table.alias('forEach', 'each');
First, we construct the new native by passing an object with a name and an initialize member to the Native constructor. The name is used for the $type() function so that we can do $type(myTable) == 'table'. The initialize function is the actual constructor for our native, and in this example, it only sets the storage object.
After the actual creation of the natives, we implement the actual methods of the native (basic things like get, set, each, etc). And to finalize things, we add an alias, so that you can do both a myTable.each and myTable.forEach.
As you might have noticed, creating a native is very similar to creating a class, and for the most part that’s true. So why are we using classes instead of creating natives?
- The Native API is private—you’re not expected to use it because it will break in the next version of MooTools.
- Natives are more appropriate for creating new types that are similar to the built-in types.
- Natives don’t offer object-oriented features that Classes offer, like mutators and mixins.
- Classes give you a cleaner syntax: you only have to pass a single object to the
Classconstructor.
But of course, there’s nothing stopping you from creating your own Natives. In fact, you might even consider using natives if you want more control via afterImplement, or if you want an easier way to add generics…
Mr. Ruffles and Sport? Nope, Cat and Dog!
Sometimes, you want to use a method from another data type on an object of another data type. Like here:
var myString = 'this_is_a_string';
for (var x = 0; x < myString.length; x++) {
var char = myString[x];
checkIfCharIsLegal(char);
}
Wouldn’t it be easier if we could just do myString.each() like an array? But because strings don’t have an each() method, we’re stuck. This is where generics come in.
A Generic Method, or generics for short, is a method of a data type that can be applied indirectly instead of calling it from an instance. Strings don’t have an each method, but arrays do—so let’s use it!
var myString = 'this_is_a_string';
Array.each(myString, function(char){
checkIfCharIsLegal(char);
});
A generic works just like the type method, but instead of using an instance of a type as the this, you pass another object you want to act as this for that function. The following lines are the same:
// as a method:
$(document.body).getElements('a');
// as a generic:
Element.getElements(document.body, 'a');
Almost all methods for both native datatypes and MooTools specific datatypes are available as generics. In fact, the Native constructor keeps another trick up its sleeve: all implemented methods are also made into generics. So in our Table code above, we also get the following generics automatically:
Table.get(table, key);
Table.set(table, key, value);
Table.erase(table, key);
Table.each(table, function, bind);
Table.empty(table);
I’m Creating New Species..
So what’s the big deal about Natives anyway? If it’s not public API, why should I care? Well, you should care because MooTools uses Native to drive one of its more powerful features: language modification. One of the things that make MooTools different from other Javascript libraries is the fact that it allows you to easily extend the language itself and add more features that aren’t available.
As you might know, the object-oriented nature of Javascript enables you to modify the language and add new features to native datatypes via modifying the prototypes. MooTools did this in older versions, so you might have seen declarations like Array.prototype.each = function().... This, of course, was both cumbersome and too destructive, and so the process was streamlined via the Native constructor.
All MooTools natives have a special method called implement, which allows you to add new methods to data types. You can use it in two ways:
// key/value way..
Array.implement('count', function(){... });
// or hash way...
Array.implement({
count: function(){
...
}
});
The second one is preferred in most cases because you’ll be able to implement several methods at once.
An example: I recently needed two methods that aren’t available in strings, rtrim and parsePath. If I was to do it using prototype, I would have ended up with something like this:
// native way...
String.prototype.rtrim = function(str){
if (this.lastIndexOf(str) == this.length - 1) {
return this.substring(0, this.lastIndexOf(str));
}
return this;
};
String.prototype.parsePath = function(){
return this.replace(/({appDir})/, system.getcwd()).rtrim('/') + '/';
};
If I wanted generics, I’d have to add this too:
// generics: native way...
String.rtrim = function(item, str){
return String.prototype.rtrim.apply(item, str);
};
String.parsePath = function(item){
return String.prototype.parsePath.apply(item);
};
Yikes! That’s too much compared to this chunk of MooTools code implements the methods and creates generics at the same time:
// MooTools way..
String.implement({
rtrim: function(str) {
if (this.lastIndexOf(str) == this.length - 1) {
return this.substring(0, this.lastIndexOf(str));
}
return this;
},
parsePath: function(){
return this.replace(/({appDir})/, system.getcwd()).rtrim('/') + '/';
}
});
// Nifty!
If you want to implement a method in several data types at once, you can use the Native.implement generic:
// Implement for multiple types
Native.implement([String, Array, Hash], {
myMethod: function(){
// ...
},
anotherMethod: function(){
// ...
}
});
MooTools also makes sure that it’s not destructive. For instance, take the forEach method. Mozilla and Webkit already have forEach methods for arrays, but IE doesn’t. If you declare the forEach method directly using Array.prototype, you’ll be overwriting the native methods from Webkit and Mozilla if you’re not careful. On the other hand, MooTools protects native methods, making sure that you don’t overwrite those methods if they’re already present.
Sometimes, this protection scheme will make you scratch your head. If you already implemented a method in one of your scripts, but you want to change it again in another script, doing something similar to this will not work:
String.implement('yes', function(){
return 'yes';
});
var yes = 'myString'.yes(); // yes == 'yes'
String.implement('yes', function(){
return 'no';
});
var no = 'myString'.yes(); // no == 'yes'
This is because String.yes() is protected. In order to modify this method, you’ll have to force MooTools to replace the method by passing true to the additional parameter of implement (aptly named force):
String.implement('yes', function(){
return 'no';
}, true);
var no = 'myString'.yes(); // no == 'no'. Hooray!
It’s a Nativity
I hope you learned some things in this post, especially about extending the language. Personally, I believe that when you stop looking for a feature and implement it yourself, you’ve crossed the threshold from being a casual MooTools hobbyist to a MooTools power-user. It’s this ease of being able to shape Javascript to a form that fits your needs that really separates MooTools from all other libraries that offer nothing but DOM-manipulation.
In the next installment of this series, we’ll learn more tips and tricks, focused on Classes. Be sure to subscribe to the RSS feed and follow me on Twitter (@keeto) for updates on the next part of the series.
Reactions
post your reactionFábio M. Costa
Really useful post for ppl that want to know how Mootools works on the inside. Really enjoyed, made somethings clearer for me and showed me some new stuff (didnt know about the force parameter on the implement function).
One thing that i would suggest is to change this:
As you might know, the object-oriented nature of Javascript…
to this:
As you might know, the prototypal nature of Javascript…
Aaron Newton
This is a spectacular tutorial. I’ve, er, written a lot about MooTools and I’ve never focused on Natives in great detail and this article makes me regret it. Natives are a really powerful aspect of the framework and I’ve basically skipped over how to create them. I think you should consider adding a lot of this to the MooTorial (which is a wiki). Regardless, great, great work.
Mark Obcena
@Fabio: I think it’s a matter of semantics really. It’s the object-oriented nature of Javascript that allows us to extend the language, much like the object-oriented nature of Ruby allows us to modify it too. Whether or not they use classical inheritance or prototypal inheritance doesn’t matter. :)
@Aaron: Thanks! I’ll find some time to check out what I can add to the MooToorial in the future.
Martin
Really good article!! Thanks a lot Mark
Oskar Krawczyk
You’re the man, Joseph!
Joseph Obcena
@Martin: Thanks!
@Oskar: Bwahaha! I’m a Joseph again, ei? :P
Vito Tafuni
there’s a way to have the alias method on Class objects?? in your opinion there’s a reason for this lack?
Mark Obcena
@Vito: Well, I have no opinion about it really—I never found a need to alias my class methods. But for future reference, here’s an implementation of an
aliasmethod for classes:A few comments on this: first, you’ll notice that we’re using
Class.prototypehere instead ofClass.implement. To avoid a complex discussion, let me just say thatClass.jsoverwrites the original nativeimplementmethod and creates a generic—which makes it impossible to add new instance methods toClasswithout usingprototype. So we implement thealiasmethod directly.Second, the code is somewhat similar to the native
aliasmethod: it could take two parameters, the original member and the alias, or a key/value hash with the format{originalName: aliasName}. For the most part, it’s lifted from the nativealiasmethod, but I’ve removed the recursive call.And finally, you’ll notice that we’re using
this.prototypedirectly to assign the new function instead of usingthis.implement. There are two reasons for this: one is that we want to bypass the additional overhead of mutator and type checks inClass.implementand second (most important), we want to bypass the wrapping process that MooTools does (this is a complex topic—but it suffices to say that lots of Class magic depend on this wrapping process). We want to bypass the wrapping process because the item we’re aliasing is already wrapped—and it would lead to weird behaviour if we repeat the process.Post a Reaction