The same code in several places is a pain. Today I’ll write a few words about repeating classes. People have come up with a solution a long time ago - you can put the same methods and properties into a common base class, and if there is none, use impurities. There are a million implementations of this pattern for JavaScript, I want to elaborate on the approach when the mixin falls into the inheritance chain.
Problem in pictures
Let's start with the visualization of our problem. Suppose we have two base classes and two child classes inherit from them.

')
At some point in the child classes there is a need for the same functionality. Normal copy-paste will look like this on our scheme:

It often happens that this functionality has nothing to do with the parent classes, therefore, it is illogical and wrong to bring it into some base class. We put it in a separate place - mixin. In terms of language, mixin can be a regular object.

And now let's discuss the moment for which the entire article is written - how to mix our mixin in classes correctly.
Based on my own experience, I can say that the most convenient way is to create a temporary class based on a mixin and substitute it into the inheritance queue.

Advantages of this approach
- ease of implementation;
- ease of redefining the code contained in the mixin;
- flexibility of connecting mixins, the ability to create dependent mixins without much difficulty;
- using another pattern in the code does not complicate its understanding and support, because the existing inheritance mechanism is used;
- intervening speed — a single cycle is not required to mix my mixin in this way;
- optimal memory usage - you copy nothing
Write the code
All subsequent examples will use the specific implementation - the Backbone.Mix library. By looking at the code, you will find that it is extremely simple, so you can easily adapt it to your favorite framework.Let's take a look at how to apply mixins that are embedded in the chain of inheritance in real life and experience the advantages of this approach in practice. Imagine that you are writing a site)) and there are different things on your site that can be closed - pop-ups, hints, etc. All of them have to listen to click on the element with the CSS class
close
and hide the element. Mixin for this might look like this:
var Closable = { events: function () { return { 'click .close': this._onClickClose }; }, _onClickClose: function () { this.$el.hide(); } };
We intervene !!!
var Popup = Backbone.View.mix(Closable).extend({
Pretty simple, isn't it? Now our inheritance chain looks like this:

- first comes the base class
Backbone.View
- an anonymous class is inherited from it, the prototype of which is the
Closable
- completes our
Popup
chain
Such a scheme makes it very easy to redefine and pre-define methods from mixin in the class to which it is mixed. For example, you can make
Popup
write something to the console when it is closed:
var Popup = Backbone.View.mix(Closable).extend({ _onClickClose: function () { this._super(); console.log('Popup closed'); } });
Here and below, the examples use the backbone-super library.Impurities that do not interfere ..
... and help. Sometimes the batch is not frail, and one mixin is indispensable. For example, imagine that we are cool guys and write a log in IndexedDB, and we also have our own
Loggable
-
Loggable
:)
var Loggable = { _log: function () {
Then to the popup, we will interfere with two mixins:
var Popup = Backbone.View.mix(Closable, Loggable).extend({ _onClickClose: function () { this._super(); this._log('Popup closed'); } });
The syntax seems to be not complicated. On the diagram, it will look like this:

As you can see, the chain of inheritance will line up depending on the order of connection of mixins.
Dependent mixins
Now imagine a situation that our analyst approaches us and says that he wants to collect statistics on all closures of pop-ups, hints - everything that can be closed. Of course, we have long been Trackback
Trackable
for such cases, from the time we did the registration on the site.
var Trackable = { _track: function (event) {
No wonder that we want to link
Trackable
and
Closable
, or rather,
Closable
should depend on
Trackable
. In our diagram, it will look like this:

And in the inheritance chain, the
Trackable
should be earlier than
Closable
:

The code for mixin with dependencies will be a bit more complicated:
var Closable = new Mixin({ dependencies: [Trackable] }, { events: function () { return { 'click .close': this._onClickClose }; }, _onClickClose: function () { this.$el.hide(); this._track('something closed');
But it became not so much more difficult, just now we have a place where we can write dependencies. For this we had to introduce an additional wrapper class -
Mixin
, and the
Mixin
itself is now not just an object, but an instance of this class. It should be noted that the mixin connection itself does not change in this case:
var Popup = Backbone.View.mix(Closable, Loggable).extend({ … });
Document mixins correctly
WebStorm has excellent mixin support. It is enough just to write
JSDoc correctly, and the autocomplete hints, understanding by the environment of the general structure of the code will noticeably improve. The medium understands the
@mixin
and
@mixes
. Let's look at an example of the documented mixin
Closable
and class
Popup
.
var Closable = new Mixin({ dependencies: [Trackable] }, { events: function () { return { 'click .close': this._onClickClose }; }, _onClickClose: function () { this.$el.hide(); this._track('something closed'); } }); var Popup = Backbone.View.mix(Closable, Loggable).extend({ _onClickClose: function () { this._super(); this._log('Popup closed'); } });
Very often, mixin is written for classes that have a specific ancestor. Our
Closable
, written for classes inherited from
Backbone.View
is by no means an exception. In such a situation, the environment will not understand where the calls of the given ancestor methods occur in the mixin code, unless it explicitly specifies
@extends
:
var Closable = new Mixin(...);
On this, perhaps all, happy intervention!
English version in my blogBackbone.Mix library
More code from the same authors:
backbonexWhat to do with jQuery noodles to bring it to mind when you can think of mixins? In english Immediately codeMy twitter (only about the code)