I'm trying to write a JavaScript library to allow inheritance with knockout.
The fundamental problem when implementing inheritance in Knockout is that each Knockout observable is its own instance, thus if you try this:
x = function () {};
x.prototype.m = ko.observable();
y = function () {};
y.prototype = new x();
z = new y();
//z.m == y.prototype.m == x.prototype.m
The behavior I'm trying to get goes like this:
//psudo-code, I know this wouldn't work
x = function () {};
x.prototype.a = ko.observable();
y = function () {};
y.prototype = new x();
y.prototype.b = ko.computed(function () { return this.a() });
//enabling this:
z = new y();
z.a(5);
a = new y();
a.a(12);
// z.b() == 5, a.b() == 12
Here's my proposed solution:
var getParams = function (args, index) {
return args.length > index + 1 ? Array.prototype.slice.apply(index) : Array.prototype.concat.call([], args[index])
};
var Ladder = function () {};
Ladder.prototype.init = function (config) {
var self = this;
if (!(config instanceof dontInit)) {
// Only initialize if config is passed, this allows us to create sub-prototypes
if (this.attachEntity) this.entity = config;
for (var prop in self) {
var extender = self[prop];
if (extender instanceof doAttach) {
self[prop] = extender.extender.apply(self, extender.args); // || ('' + extender.extender); // if the function doesn't return a value perhaps it only performs operations? Leave a tatle tale behind?
}
}
if (this.populateObject) {
for (var prop in config) {
if (config.hasOwnProperty(prop)) {
var val = config[prop];
if (ko && ko.isObservable(self[prop])) self[prop](ko.unwrap(val));
else self[prop] = val;
}
}
}
for (var prop in self) {
var extender = self[prop];
if (extender instanceof doExtend) {
self[prop] = extender.extender.apply(self, extender.args); // || ('' + extender.extender); // if the function doesn't return a value perhaps it only performs operations? Leave a tatle tale behind?
}
}
}
}
Ladder.prototype.attachEntity = true;
Ladder.prototype.populateObject = true;
Ladder.prototype.attach = function (propertyName, extender) {
var args = getParams(arguments, 2);
this.prototype[propertyName] = new doAttach(extender, args);
}
Ladder.prototype.extend = function (propertyName, extender) {
var args = getParams(arguments, 2);
this.prototype[propertyName] = new doExtend(extender, args);
}
Ladder.inherit = Ladder.prototype.inherit = function () {
var ctor = function (config) {
this.init(config);
};
ctor.attach = Ladder.prototype.attach;
ctor.extend = Ladder.prototype.extend;
ctor.inherit = Ladder.prototype.inherit;
ctor.prototype = new this(new dontInit());
return ctor;
};
Ladder.prototype.attachComputed // doComputed
Ladder.createPrototypes = function (prototypes) {
return duplicatePrototypes(prototypes);
}
var dontInit = function () {};
// doAttach is called before config and should attach a computed or observable, though you may attach any value you like.
var doAttach = function (extender, args) {
this.extender = extender;
this.args = args;
};
var doComputed = function (computed) {
return new doExtend(function () {
return ko.computed(computed, this);
}, []);
};
// doExtend is called after config and would normally perform some init work
// naming convention for extenders is _00Name where 00 is a priority (00 will run before 01) and Name is a descriptive name
var doExtend = function (extender, args) {
this.extender = extender;
this.args = args;
};
My questions are:
Are there performance issues with this type of inheritance? i.e. if I use this paradigm to build complex objects with long inheritance chains, will there be a performance hit vs some other 'better' inheritance paradigm?
Does this make for clear code? Is it obvious what is going on when I define an object like this:
var myObject = usefullBaseClass.inherit(); myObject.attach("newProperty", ko.observable, 5); var instanceOfMyObject = new myObject(); // instanceOfMyObjecct is an instance of myObject and has an observable // property called newProperty which is unique to this instance