chic icon indicating copy to clipboard operation
chic copied to clipboard

Extend class without inheriting, something like an "include" functionality of sorts

Open darkguy2008 opened this issue 11 years ago • 6 comments

Hey!

I've chosen this library over a lot of others I've seen due to its ease of use and functionality. However, I'm in need of something that I bet it would make this even more powerful without getting bloaty (like the rest of class libs):

Let's say I have a class:

var myParent = Class.extend({ func1: function() { ... } func2: function() { ... } })

And it's getting long and long and longer. Let's "split" it in another .js file, doing something like:

myParent.extendEvenMore({ func3: function() { ... } func4: function() { ... } })

So, one could do:

myParent.func1(); myParent.func2(); myParent.func3(); myParent.func4();

I wonder if it's possible, or do you have a theory on how to do it so I can attempt to fork & code it?

Thanks again for this awesome lib!

darkguy2008 avatar Feb 28 '14 22:02 darkguy2008

Hi José, yes I like this idea. It's definitely achievable, the API would have to change, but it's just a case of adding all the methods defined in extendEvenMore to the class prototype. The API would have to look more like this:

var MyClass = Class.extend({
    func1: function() { ... }
    func2: function() { ... }
});

MyClass.reopen({
    func3: function() { ... }
    func4: function() { ... }
});

Currently this is possible without the reopen method by simply extending the prototype of your new class like so:

var MyClass = Class.extend({
    func1: function() { ... }
    func2: function() { ... }
});

MyClass.prototype.func3 = function() { ... };
MyClass.prototype.func4 = function() { ... };

rowanmanning avatar Mar 03 '14 10:03 rowanmanning

Well, first of all thanks for your reply!

Your prototype suggestion was actually very helpful. It light a bulb about a hour ago and I managed to do it (I think :P). According to my quick tests, the code seems OK. Here it is! I know this might sound annoying, but if you could please check it, I'd be really glad and shall make a pull request if possible :).

Just add this method to chic.js:

    Class.reopen = function(props) {
        var Parent = this;
        var extendingFlag = '*extending*';
        var proto;

        // Extension
        Parent[extendingFlag] = true;
        proto = new Parent();
        delete Parent[extendingFlag];

        // Add new properties
        forEachProp(props, function (value, name) {
            if (isFn(value)) {
                Object.getPrototypeOf(proto)[name] = applySuperMethod(value, Parent.prototype[name]);
                return;
            }            
            Object.getPrototypeOf(proto)[name] = value;
        });

        // Construct
        function Class () {
            if (!Class[extendingFlag] && isFn(proto.init)) {
                proto.init.apply(this, arguments);
            }
        }

        // Add extend method and return
        Class.prototype = proto;
        Class.prototype.constructor = Class;
        Class.extend = Parent.extend;
        Class.reopen = Parent.reopen;
        return Class;
    };

And also add "Class.reopen = Parent.reopen;" to the Class.extend constructor so you can apply the reopen function, of course.

Sample app :)

var Class = chic.Class;

var MyClass = Class.extend({ 
    init: function() {
        this.lulz = false;
        console.log("OMG, lulz?", this.lulz);
    }
});

MyClass.reopen({
    omg: function() {
        this.lulz = true;
        console.log("Extended OMG!. Lulz? ", this.lulz);
    }
});

function Game() {
    var omg = new MyClass();
    omg.omg();
}

This outputs: 20:52:03.322 "OMG, lulz?" false game.js:6 20:52:03.322 "Extended OMG!. Lulz? " true game.js:13

So yay! :D

darkguy2008 avatar Mar 10 '14 01:03 darkguy2008

Cool, thanks José, this seems to work. If you decide to open a pull-request, I'd be a little concerned with the repetition between Class.extend and Class.reopen, I'm sure we could somehow abstract the code away and cut down on number of lines. Let me know if you're intending on doing this, otherwise I'm happy to do it when I get some spare time.

rowanmanning avatar Mar 15 '14 21:03 rowanmanning

Alright Rowan, you were right. The change was actually very tiny, so I managed to do it using a simple conditional in the same extend method. The Class.extend method now can be used to create a new class ( var omg = Class.extend({}); ) or to add new stuff to it ( omg.extend({ }); ).

IMHO it can be a bit confusing at first, but hey, the change is only 3 lines and two variables!

I'd be glad if you can merge this into your main code. I used this change against my game library (which is about 15+ js files with almost half of it being classes extended with my method) without any problems whatsoever, the functionality was kept intact, I only had to change ".reopen" to ".extend" and voilá. :)

If there's anything else, let me know! :D

darkguy2008 avatar Mar 17 '14 22:03 darkguy2008

Hi José, I'm busy for the next couple of days but I'll try to take a look at this later in the week. Thanks

rowanmanning avatar Mar 17 '14 23:03 rowanmanning

It's okay, I understand :). Also, submitted a quick fix, now it seems to build correctly in Travis :D. You're welcome! :D

darkguy2008 avatar Mar 18 '14 01:03 darkguy2008