Chimeces
Completely lost

Extending classes in js using mixins

There are times when we have different classes that share a certain behaviour but they do not have a common ancestor.

Taking advantage of JS prototypal inheritance, we can make them share code/behaviour and avoid duplication .

Example case

Imagine we have two random types

var Person = function (name) {
    this.name = name;
};

var Car = function (brand, model, year) {
    this.brand = brand;
    this.model = model;
    this.year = year;
};

If we wanted make both Persons and Cars blow up, we would have to create a super class (not a good idea since this two types do not have anything we want in common) or implement the behaviour in both classes (code duplication).

A solution using mixins

The solution we are going to explore is Mixins. A mixin is a standalone piece of functionality or behaviour. It has to exist in a object form, so that it can be used later to extend other types prototypes.

For example, for the explode exercise:

If we use what we will call a mixin, then we get what we wanted. If we define the explode functionality as a standalone mixin:

var ExplodeMixin = {
    blown: null,
    blowUp: function() {
        if (this.blown === null) {
            console.log('KaBoww');
            this.blown = (new Date()).getTime();
        } else {
            console.log('Already blown up');
        }
    }
};

Then we could extend our types with this behaviour without duplicating code or creating a common ancestor. For it, we are going to use a function called extend, that basically extends a object with another one. Check the underscorejs docs for example -> _.extend

var Person = function (name) {
    this.name = name;
};
_.extend(Person.prototype, ExplodeMixin);

var Car = function (brand, model, year) {
    this.brand = brand;
    this.model = model;
    this.year = year;
};
_.extend(Car.prototype, ExplodeMixin);

After this, we can safely do:

var john = new Person('John');
john.blowUp();

KaBoww

var golf = new Car('VolsWagen', 'Golf', 2012);
golf.blowUp();

KaBoww

Using it with Backbone

The case we found for using this pattern was for example, when among multiple views you want to have a certain behaviour shared, like in our case with putting a view in a loading state.

So our real example looks like this:

views/mixins/loading.js var LoadingMixin = { loadingOverlayTemplate: require(‘views/templates/loading_overlay’),

    isLoading: false,
    
    setLoading: function(yes) {
        if (yes) {
            if (!this.isLoading) {
                this.isLoading = true;
                $(this.el).html(this.loadingOverlayTemplate());
            }
        } else {
            if (this.isLoading) {
                this.isLoading = false;
                this.$('.flOverlay').remove();
            }
        }
    }
};

module.exports = LoadingMixin;

views/communication_actions.js var LoadingMixin = require(‘views/mixins/loading’)

var CommunicationActionsView = CommunicationView.extend({
    
    /*
     * Code for the view here.
     */
    
    formSubmit: function() {
        
        // Method of the loading mixin
        this.setLoading(true);
        
        // More stuff
    }
});

_.extend(CommunicationActionsView.prototype, LoadingMixin);

module.exports = CommunicationModalStep1View;

This way, you can share behaviours or code among certain types as you want.

blog comments powered by Disqus