Extend class without inheriting, something like an "include" functionality of sorts
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!
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() { ... };
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
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.
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
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
It's okay, I understand :). Also, submitted a quick fix, now it seems to build correctly in Travis :D. You're welcome! :D