Inheritance in Javascript (one way)
So, I recently posted a tip on how I write classes. So someone asked me how I use inheritance there. Well, one of the functions that I really like comes from the ACE source. You can look at it here. I'm going to paste an excerpt of the function too:
/* From the ACE Source code
Distributed under the BSD license:
Copyright (c) 2010, Ajax.org B.V.
All rights reserved.
*/
exports.inherits = (function() {
var tempCtor = function() {};
return function(ctor, superCtor) {
tempCtor.prototype = superCtor.prototype;
ctor.super_ = superCtor.prototype;
ctor.prototype = new tempCtor();
ctor.prototype.constructor = ctor;
};
}());
When I use this particular method, here's the flow:
var Base = function() {
};
(function() {
this.method = function method() {
return 'BASE';
}
}).call(Base.prototype);
The derived class then:
var Derived = function() {
};
oop.inherits(Derived, Base);
(function() {
}).call(Derived.prototype);
Now, I have to do this between the constructor and the prototype definition because if you look at the inherits
code, you'll see that it completely overrides the prototype. Therefore, I just let the inheritance
method copy the base class' prototype, and then go ahead and modify it on my own. Here's another strong point with writing classes the way I do, I'm just modifying the prototype, not re-writing it from scratch. However, the big caveat here is that if you want to use the super class method, you'd have to do something like this within a method:
Derived.super_.method.call(this);
I don't have problems with this, but don't like the way I have to mention the class name every time to call the parent method. It's a little more verbose than I like. Though, it definitely works. No issues there.
Ok. Now the other big method I really like is written here by John Resig (the guy who made jQuery). It's an excellent article to read if you ever want to brush up/ learn about inheritance in javascript.
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
// Create a new Class that inherits from this class
Class.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();
Read through the comments and you'll get a pretty good idea what's going on here. Though there is one problem here. He's written the code thinking we'll all go around deriving from a base class and writing code like this:
var Person = Class.extend({
init: function(isDancing){
this.dancing = isDancing;
}
});
var Ninja = Person.extend({
init: function(){
this._super( false );
}
});
var p = new Person(true);
p.dancing; // => true
var n = new Ninja();
n.dancing; // => false
Not that there's anything wrong with the above, I just don't want to have to do this to get inheritance working. So, I messed around and merged the two methods into something I like.
/* JavaScript Inheritance
* Author: Mutahhir Ali Hayat
* Made by joining some parts by John Resig http://ejohn.org/ and some by Ajax.org Code Editor (ACE)
*/
define(function(require, exports, module) {
var initializing = false,
fnTest = /xyz/.test(function() {
xyz;
}) ? /\b_super\b/: /.*/;
exports.inherits = (function() {
var tempCtor = function() {};
return function(ctor, superCtor) {
tempCtor.prototype= superCtor.prototype;
var prop = ctor.prototype,
_super = superCtor.prototype,
prototype = new tempCtor();
for(var name in prop) {
prototype[name] =
typeof prop[name] === "function" &&
typeof _super[name] === "function" &&
fnTest.test(prop[name])?
(function(name, fn){
return function(){
var tmp = this._super;
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
}
})(name, prop[name]) : prop[name];
}
ctor.prototype = prototype;
ctor.prototype.constructor = ctor;
return ctor;
}
})();
});
How you use it is mentioned in the Gist I created (forgot I wasn't signed in, and it didn't ask me, so Anonymously posted :))
Basically... the major change is that instead of sandwiching the inheritance call between the constructor and prototype definition, you have to do something like this:
var Derived = function() {
}
(function() {
}).call(Derived.prototype);
inherits(Derived, Base);