modelo

A JavaScript object inheritance utility.

Modelo.js

A multiple inheritance utility for JavaScript.

Inheritance libraries today all seem to enforce the same clunky interface style. The only one of any merit these days is 'util.inherits' from the Node.js standard library. Only problem: no multiple inheritance.

Wouldn't it be great if 'util.inherits' supported multiple inheritance and it stayed fast too?

That's this library. That's why it exists.

The 'modelo.inherits' function can act as a drop in replacement for 'util.inherits'. Already have a code base that you want to start extending? No problem.

 
    var modelo = require('modelo');
 
    function Base() {
        // Base object constructor 
    }
    Base.prototype.baseMethod = function baseMethod() {
        console.log('Method from base object.');
    }
 
    function Extension() {
        // Sub-object constructor 
    }
    // util.inherits(Extension, Base); 
    modelo.inherits(Example, Base);
 
    new Extension() instanceof Base; // true 
 

Once you need to extend multiple base objects, just put more base objects in the call to 'inherits'.

 
    var modelo = require('modelo');
 
    function MixinOne() {}
    function MixinTwo() {}
 
    function Combined() {}
    modelo.inherits(Combined, MixinOne, MixinTwo);
 
    var instance = new Combined();
 
    instance.isInstance(Combined); // true 
    instance.isInstance(MixinOne); // true 
    instance.isInstance(MixinTwo); // true 
 

Unfortunately, there is no way to make 'instanceof' work with multiple inheritance. To replace it, simply use the 'isInstance' method that gets added to your instances. It will return true for any base object in the inheritance tree.

All inheritance libraries have their cost. When the overhead in question affects the speed of object definition and creation, though, that cost must be kept to a minimum. Here is how this library compares to the competition:

The typical benchmark you will see while researching inheritance tools is one that measures the cost of an object prototype, or class, definition followed by the creation of a single instance. The following results are based on a test which does just that. Each library produces an equivalent inheritance tree and spawns an instance. The full source of the benchmark can be found in 'benchmarks/comparisons/define.js'.

The approximate results:

Name% Slower
Fiber0.0000 %
util.inherits24.010 %
augment64.601 %
Modelo65.594 %
Klass74.658 %

The Fiber library is the clear winner with a 24% difference in run-time cost from the Node.js 'util.inherits'. Considering the implementation of 'util.inherits' is effectively a two line wrapper around the 'Object.create' built-in, it's quite a surprise that Fiber is that much faster. Now, the actual difference between Fiber and 'util.inherits' is something on the order of ~0.00008 seconds which, frankly, is inconsequential.

In fact, even the difference between Fiber and the bottom three libraries is inconsequential, not because the difference is not statistically significant but, because this benchmark only represents the time required to define a "class", or object prototype. This is something that happens, at most, once for each class, or object prototype, defined in a code base. These run-time costs simply do not matter unless your code base generates hundreds of thousands of "class" definitions.

A far more realistic measurement of overhead is the time it takes to create an instance of an object defined using an inheritance library. After all, creating instances necessarily happens far more often than defining the prototype:

Name% Slower
Modelo0.0000 %
util.inherits3.4355 %
Fiber45.017 %
augment48.284 %
Klass161.79 %

The above results are deceptive. While it appears as though Modelo is faster than the others, including the Node.js 'util.inherits', the reality is that the run-time difference between these libraries is so small that it exceeds the microsecond resolution of the timer used in the benchmarks. For all intents and purposes there is no measurable difference between any of these libraries.

When it comes down to it, you should pick your inheritance tool chain based on its interface. The run-time cost of most inheritance libraries on the market today is sub-microsecond and unlikely to affect the performance of your code.

Note: If you find a flaw in any of the benchmarks used please open an issue on GitHub.

This package is published through NPM under the name 'modelo':

$ npm install modelo

Once installed, simply 'require("modelo")'.

This module uses browserify to create a browser compatible module. The default grunt workflow for this project will generate both a full and minified browser script in a build directory which can be included as a <script> tag:

<script src="modelo.browser.min.js"></script>

The package is exposed via the global name 'modelo'.

Running the npm test command will kick off the default grunt workflow. This will lint using jslint, run the mocha/expect tests, generate a browser module, and generate browser tests.

Running grunt benchmark will run the benchmarks discussed above. You can optionally install the micro-time library (npm install microtime) to get microsecond precision.

This project is released and distributed under an MIT License.

Copyright (C) 2012 Kevin Conway

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

All code must validate against JSlint.

Mocha plus expect. All tests and functionality must run in Node.js and the browser.

All contribution to this project are protected by the contributors agreement detailed in the CONTRIBUTING file. All contributors should read the file before contributing, but as a summary::

You give us the rights to distribute your code and we promise to maintain
an open source release of anything you contribute.