Mixins on prototype level #10

Open
nejcjelovcan opened this Issue Jun 21, 2012 · 4 comments

Projects

None yet

2 participants

@nejcjelovcan

Would be nice if mixins were processed on prototype level rather than on object level (in initialize method). This way, Button class would only have one _observeModel reference instead of each instance having their own.

@inf3rno
inf3rno commented Jun 3, 2013

It is hard to solve this, because there is no multiple inheritance in javascript. So if you mixin some "abstract class" or "interface", the further modification of the mixed classes won't be inherited to the previously created descendant. This can be solved partially if you create the options property of every class with Object.create(superClass.prototype.options). About 99% of the cases the mixed "interfaces" like HasModel, HasAlternativeProperty etc... are so abstract, that the options property of them contains only null values. So I think this should work. Btw not only the options should inherit properties, e. g. by classNames it would be useful too... I think I'll create a Backbone.UI.View class, and create a custom static extend function. I'll move the other Backbone.View stuff to there, so this lib won't pollute Backbone.View with custom methods anymore... Btw I think the author of this lib left us, he has not answered for months...

@inf3rno
inf3rno commented Jun 3, 2013

Create the basic implementation of this. It works only on the options property now...

Backbone.UI.View = Backbone.View.extend({
    options: {}
}, {
    extend: function (instanceProps, staticProps) {
        var proto = this.prototype;
        var aggregatedInstanceProps = {};
        aggregatedInstanceProps.options = Object.create(proto.options);
        if (instanceProps)
            _(_.isArray(instanceProps) ? instanceProps : [instanceProps]).each(function (props) {
                _(props).each(function (value, property) {
                    if (property == "options")
                        _.extend(aggregatedInstanceProps.options, value);
                    else
                        aggregatedInstanceProps[property] = value;
                });
            });
        var aggregatedStaticProps = {};
        if (staticProps)
            _(_.isArray(instanceProps) ? instanceProps : [instanceProps]).each(function (props) {
                _.extend(aggregatedStaticProps, props);
            });
        return Backbone.View.extend.call(this, aggregatedInstanceProps, aggregatedStaticProps);
    },
    defaultOptions: function (options) {
        _.extend(this.prototype.options, options);
    }
});

Usage:

//inheritance:

var Backbone.UI.MyComponent = Backbone.UI.View.extend([
    Backbone.UI.HasModel,
    Backbone.UI.HasAlternativeProperty,
    {
        options: {
            //...
        },
        initialize: function (){
            //...
        },
        render: function (){
            //...
        }
    }
]);

//for i18n support:

Backbone.UI.MyComponent.defaultOptions({
    emptyContent: "Üres - means empty in Hungarian"
});

I'll create tests and override existing classes on my fork ASAP.

@inf3rno
inf3rno commented Feb 8, 2014

I created a lib to solve this kind of issues once and for all:
https://github.com/inf3rno/mixin.js

It contains prototypal and multiple inheritance as well. Just a short example:

    window.Backbone.UI.RadioGroup = Backbone.UI.BaseView.extend({
        //...
        initialize : function(options) {
            Backbone.UI.BaseView.prototype.initialize.call(this,
                options
            );
            this.mixin([
                Backbone.UI.HasModel,
                Backbone.UI.HasAlternativeProperty,
                Backbone.UI.HasGlyph,
                Backbone.UI.HasFormLabel,
                Backbone.UI.HasError
            ]);
            //...
        },
        //...
    });

could be something like this:

    window.Backbone.UI.RadioGroup = Backbone.UI.BaseView.extend(
        Backbone.UI.HasModel,
        Backbone.UI.HasAlternativeProperty,
        Backbone.UI.HasGlyph,
        Backbone.UI.HasFormLabel,
        Backbone.UI.HasError,
        {
            //...
            constructor: function(options) {
                //...
            }
            //...
        },
        //...
    });

I am still developing it, and testing the idea on my projects.

The only constraint currently, that it always runs all of the ancestors constructor. Maybe I'll undo that, I am not sure yet, that's why I'm testing... ;-) I usually use setters instead of constructor injection, or I use constructor injection with config objects like options here, so this autorun feature is good for my coding style...

@inf3rno
inf3rno commented Feb 8, 2014

I reconsidered the automatic parent constructor call both by multiple inheritance and prototypal inheritance, and ended up by removing them from my lib. inf3rno/o3#3 This fix will come with the 1.1.0 version. It will make the lib more flexible, but ofc more error prone as well, because it is easy to accidentally forget a constructor call by multiple inheritance...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment