Create Namespaced Classes with MooTools

By  on  

MooTools has always gotten a bit of grief for not inherently using and standardizing namespaced-based JavaScript classes like the Dojo Toolkit does.  Many developers create their classes as globals which is generally frowned up.  I mostly disagree with that stance, but each to their own.  In any event, namespaces are technically just nested objects and I recently shared with you how to create and retrieve nested objects with MooTools.  With those utility functions available, I've modified Class so that you can create namespaced classes easily!

The MooTools JavaScript

The technique below is very much like monkey patching:

(function() {
	
	// Keep reference to the original Class
	var klass = Class;
	
	// Redefine class ("that's deep")
	Class = function(name, params, context) {
		// Find out if this is namespaced or the original method
		var namespaced = (arguments.length > 1);
		
		// Add the class name to the param list
		if(namespaced) params.$name = name;
		
		// Create and get the original class
		var original = new klass(namespaced ? params : name);
		
		// If namespaced, set class into namespace
		if(namespaced) Object.place(name, original, context);
		
		// Return this newly created class!
		return original;
	};
	
})();

I keep a reference to the original Class, as I'll be using it within my new Class function.  The new signature allows for a namespaced class name, the usual class properties, and the context which may be passed to the Object.place method.  The first step is analyzing the arguments provided to Class;  if one argument, it's a traditional class;  if more than one argument is provided, we know we're trying to create a namespaced class and will act accordingly.  You'll also notice that I add a $name property to the class prototype, providing the given class name to the class for later use.  I've found the class' declaredClass property incredibly helpful within Dojo.

Here's how you'd use the new Class function:

// Create a namespaced class
var myClass = new Class("davidwalsh.ui.Slider", {
	initialize: function() { 
		// Do stuff!
	},
	doSomething: function(){}
});
// myClass === davidwalsh.ui.Slider!

// Create an instance of the class
var myInstance = new davidwalsh.ui.Slider({
	// props
});

// Get the name of the declared class
var instanceClassName = myInstance.$name; // equals "davidwalsh.ui.Slider"

The first argument becomes the namespace and class name as a string.  The second argument is the traditional parameters argument.  If only one argument is provided to the Class function, the class is created and returned per usual;  in fact, both the namespaced method and traditional method return the newly created class.  The method provided above is completely backward compatible so you wont have rewrite your existing code.

Namespaced classes are probably a good idea, as many would argue that "polluting" the global namespace with numerous objects can lead to problems.  In any event, this namespacing method should help you make the most of my set/get nested object methods and allow you to keep the global namespace as clean as you'd like!

Recent Features

  • By
    How I Stopped WordPress Comment Spam

    I love almost every part of being a tech blogger:  learning, preaching, bantering, researching.  The one part about blogging that I absolutely loathe:  dealing with SPAM comments.  For the past two years, my blog has registered 8,000+ SPAM comments per day.  PER DAY.  Bloating my database...

  • By
    39 Shirts – Leaving Mozilla

    In 2001 I had just graduated from a small town high school and headed off to a small town college. I found myself in the quaint computer lab where the substandard computers featured two browsers: Internet Explorer and Mozilla. It was this lab where I fell...

Incredible Demos

Discussion

  1. Alexander

    Interesting article !

    I’m not a namespacing expert, so here goes nothing :

    The big advantage of such a style of class naming is that instead of having a global variable holding each class, the globale namespace is instead polluted with the topmost level of each namespaces used (assuming we have some from core, some from more, some from a few other third party sources and so on…). Plus we add the overhead to lookup properties of properties (it has been proven that it takes more time to access object.child1.child2 than just object). So as I understand it, it gives the clearer categorization of classes (which is the idea behind Request.HTML, Request.JSON already) and reducing a bit the amount of polution of the global namespace, right ?

  2. Alexander

    Sorry to spam the post, but I thought about all this again and wonder about something else.

    1) the variable var myClass in your example will be in the global namespace, so how using namespacing will reduce pollution ?

    2) Where is Object.place defined ? it’s not in the Core or More documentation, nor in JS itself ?

    3) How is using this monkey patching snippet you posted different from doing :

    var davidwalsh = {ui: {}};
    
    davidwalsh.ui.Slider = new Class({
      initialize: function() { 
        // Do stuff!
      },
      doSomething: function(){}
    });
    
    
  • 1. I put the “var myClass =” there to prove that the new method returns the created class like the old method does, even if you use a namespace. That’s why I added “// myClass === davidwalsh.ui.Slider!” as a code comment.

    2. That code lives at the following link, which is right at the top of the post: http://davidwalsh.name/mootools-namespace

    3. Creating nested objects is tedious. And what if you want to override a class if it’s there, create it if it’s not? You’d use a bunch of if statements to create the nesting. This is far more efficient.

  • Phil Carty

    Nice, clever little tutorial. I had been “unconsciously” using the same as Alexander’s #3 solution of simply having nested objects for the sake of namespacing classes… and as you say, it’s cumbersome and doesn’t feel like a very efficient way of doing things while you code.

    Will be using this technique from now on! Thanks.

  • Phil Carty

    Nice, clever little tutorial. I had been unconsciously using the same as Alexander’s #3 solution of simply having nested objects for the sake of namespacing classes… and as you say, it’s cumbersome and doesn’t feel like a very efficient way of doing things while you code.

    Will be using this technique from now on! Thanks.

  • Eric C.

    I found this to be very useful when working on an e-commerce site. I’ve organized it as sitename.classes.Checkout or sitename.classes.Default. Where sitename.classes.Checkout contains the class for checkout code and sitename.classes.Default is site-wide functionality.

    My question is, any suggestion on how to organize the instances of the above classes when they are created?

    For example (assuming this lives inside of a domready event):

    //no namespace, just floating out there
    myclass = new sitename.classes.Checkout({});
    var myclass = new sitename.classes.Checkout({});

    //namespaced
    sitename.fn.Checkout = new sitename.classes.Checkout({});

    //or, just forget about it and do
    new sitename.classes.Checkout({});

    Side note, I’m using Class a bit incorrectly here. I could just create an object to be a bit leaner, since it’s unlikely that I’ll be re-using much of this code.

  • hanishi

    Hi great blog! Did not come to an idea of “monkey patching” until I found this article. I sort of needed method overload feature in MooTools system as I was porting Java library to MooTools and below is what I have achieved so far. It is a derived version of the famous method overload done by John Resig.
    Since you seem to know the internals of MooTools very well, I was wondering and appreciate very much if you can help me correct or give an advise how it is done in the code below will not produce any side effects. I wanted to protect the “overload” method to be called once it is “finalized” so I had to work on the “wrapper” function.

    (function(){
        var overloaded = function() {
            this.$overloaded = true;
            return this;
        };
    
        Function.implement({
    
            overload: function(fn) {
                if (this.$finalized)
                    throw new Error('The method "overload" cannot be called.');
                if (typeOf(fn) !== 'function' || fn.$overloaded)
                    throw new Error('The method "overload" failed');
    
                var prev = this,
                    next = function() {
                        if (fn.length == arguments.length)
                            return fn.apply(this, arguments);
                        else if(typeOf(prev) === 'function')
                            return prev.apply(this, arguments);
                    };
                return overloaded.call(next);
            },
    
            finalize: function() {
                this.$finalized = true;
                return this;
            }
        });
    
        var wrap = function(self, key, method){
            if (method.$origin) method = method.$origin;
            var wrapper = function(){
                if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
                var caller = this.caller, current = this.$caller;
                this.caller = current; this.$caller = wrapper;
                var result = method.apply(this, arguments);
                this.$caller = current; this.caller = caller;
    
                return result;
            }.extend({$owner: self, $origin: method, $name: key});
            if (method.$finalized)
                wrapper.finalize();
            return wrapper;
        };
    
        var implement = function(key, value, retain){
            if (Class.Mutators.hasOwnProperty(key)){
                value = Class.Mutators[key].call(this, value);
                if (value == null) return this;
            }
    
            if (typeOf(value) == 'function'){
                if (value.$hidden) return this;
                this.prototype[key] = (retain) ? value : wrap(this, key, value);
            } else {
                Object.merge(this.prototype, key, value);
            }
    
            return this;
        };
    
        Class.implement('implement', implement.overloadSetter());
    })();
  • Shawn

    My limited insight into this is there should be 4 nests never more than one deep.. shared.basetemplate class.localtemplate class.protectedtemplate class.privatetemplate.. then when you declare and construct/create an object it builds it’s set of properties and functions from all the templates with that class name and from the shared template.. I am not sure how mooTools does it but I would use an array with classnames as key and directly find the definition of each quickly and I lean more to dynamically adding functions to applications and sharing data / classes / functions between them without accidentally calling a function from a different app… so it is likely an app creates it’s objects if they don’t exist in the correct namespace. Not sure how MooTools works, and this may not make sense, but I am working on a configuration that allows apps to work independently in the browser but still share classes, dataobjects and standard server request functions, while maintaining independent functionality. but I do things different, my application would maintain keyed arrays of objects under the appname.object so finding the object should be quick, most of it’s work happens at init time when libraries are checked and loaded and it’s private libs loaded the libs would be the classes it needs as some would be shared classes and other classes would be loaded under its own name, while yet some would be system classes and I find unlikely to check other classes unless user defined classes become available where they are entered by the user during the session, I however don’t find an app would dependent on anything 3rd party but could use as plugin or extra functionality if existed… just thinking

  • Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!