Javascript Inheritance, v2
In an earlier post, I presented a mechanism for simulating javascript inheritance. It was, of course, incorrect. I tested the implementation and believed it to work, but found out shortly thereafter that I was wrong. However, this leads me into a discussion about javascript simulated inheritance, and a presentation of the correct solution also.
Inheritance, methodology 1
There are several approaches that exist to simulate inheritance, ranging from simple and effective, to extremely complex. This first method is quite simple, and is a feature that, were it in formalized object oriented languages in some form, would be quite useful, though possibly destructive in regards to the standard oo features.
This method involves simply copying properties (namely functions) from one object to another. This can be used to temporarily enhance an instance of an object, or to perform "mixins" of one function group into another function group. The mixin feature would, in a formalized oo language, be referred to as multiple inheritance. It is of course mostly impossible to do in said languages because of its implications on polymorphism. Here is the borrow mechanism in javascript.
Object.borrow = function(destination, source) {
for (var key in source) {
destination[key] = destination[key] || source[key];
}
return destination;
};Inheritance, methodology 2
The second method resembles the first, except that it specifically targets the prototype of two existing classes (constructor functions).
Object.augment = function(destination, source) {
for (var key in source.prototype) {
destination.prototype[key] = destination.prototype[key] || source.prototype[key];
}
return destination;
};Inheritance, methodology 3
The third method involves something slightly more complex, for much grander results. This approach is what I consider to be the most popular and classical implementation of javascript simulated inheritance. It is known by most as constructor chaining. In my opinion, this is actually a convenience around constructor chaining, since you could do that without any implementation at all. In fact, the real heart of this implementation is creating a class (constructor function) whose prototype is in fact an instance of another class. Using the instanceof operator would yield expected results when operating on an instance of the child class. However, the constructor chaining feature is provided for simple convenience, and doesn't add anything to this implementation.
Object.extend = function(destination, source) {
var obj = function() {};
obj.prototype = source.prototype;
var proto = destination.prototype;
destination.prototype = new obj();
destination.prototype.constructor = destination;
for (var key in proto) {
destination.prototype[key] = proto[key];
}
destination.prototype.superclass = source;
return destination;
};Inheritance, methodology 4
The final method is where I've attempted to go, and had at first failed miserably. This final attempt has yielded the true implementation I was looking for, though it contains one very small but disappointing drawback.
First, you should notice immediately that this implementation allows for more than just constructor chaining (which is always via the Function.prototype.call or Function.prototype.apply methods in the ECMAScript standard). In fact, this implementation does all of that for you, and so in my opinion truly simulates inheritance, where the other implementations were just a hearty attempt to bring (in)convenience to something that was already altogether inconvenient in the first place.
Unfortunately, this approach does call for one stipulation. The function called superclass is added to the prototype of the subclass, which is analogous to the super keyword in java. This function MUST be called as the first thing the subclass constructor does, or else nothing works that you intended to work with inheritance. Well, that's not completely true, but the true features of this implementation would not be present. This is because javascript scoping is terribly painful, and there would be no way to bind a superclass function to being executed in the scope of a subclass without access to the instance of the subclass. This instance can only be obtained at instantiation time, or sometime thereafter. Apropos, an instance of the subclass will be dynamically added to the superclass variable, and all dynamically bound functions of the superclass will be called in the scope of this subclass variable when called using the superclass variable from the subclass. *whew*
Object.inherit = function(destination, source) {
var obj = function() {};
obj.prototype = source.prototype;
var proto = destination.prototype;
destination.prototype = new obj();
destination.prototype.constructor = destination;
for (var key in proto) {
destination.prototype[key] = proto[key];
}
var constructor = function() {
this.superclass.subclass = this;
this.superclass.prototype.constructor.apply(this, arguments);
};
destination.prototype.superclass = constructor;
destination.prototype.superclass.prototype.constructor = source;
for (var key in source.prototype) {
destination.prototype.superclass[key] = (function(__method) {
return function() {
return __method.apply(this.subclass, arguments);
};
})(source.prototype[key]);
}
return destination;
};
0 comments:
Post a Comment