JavaScript Extension / Inheritance: jsExtender





5.00/5 (2 votes)
An easy to use JavaScript Inheritance / Extension Library.
Introduction
While working on a few projects I noticed I needed a better way to extend my JavaScript code. I did a lot of research online and wasn't really able to find a solution that fit all of my needs. Some supported simple inheritance and others attempted to support multiple inheritance and fell short. This is my attempt to find a better solution to the inheritance problem.
The full code and examples can be found on GitHub: https://github.com/dredmond/jsExtender.
Background
My goal was to create something that was elegant, easy to use, and worked in almost every possible case. While working on a solution I went through two different iterations and this is the one I stuck with and named jsExtender. The other version I wasn't happy with since it followed a lot of the same patterns I had found and disliked, but it gave me the inspiration to create jsExtender.
Using jsExtender.js
Default Usage:
Creating a default extension with no other properties or functions is simple. While not entirely useful by itself it allows for easy extensions later. It is similar to the Object
class.
// Using jsExtender's default constructor.
var defaultClass = jsExtender();
defaultClass.prototype.testFunc = function () {
console.log('Testing the test function!');
};
// Create a defaultClass object.
var defaultInstance = new defaultClass();
defaultInstance.testFunc();
Extending the defaultClass
using an object:
I wanted class extensions and inheritance to be clean to look at and easy to use. JavaScript is hard to maintain and having ugly inheritance structures all over the place won't help much. In order to inherit or extend a class you just have to call defaultClass.extend(<function or object>);
It can't get much easier than that.
// Extend the default class and override the constructor.
var defaultClassExtended = defaultClass.extend({
constructor: function () {
// Override default constructor.
console.log('The default constructor has been overridden!');
}
});
// Create a new instance of the extended default class.
defaultInstanceExtended = new defaultClassExtended();
// The testFunc declared on defaultClass should still work in defaultClassExtended.
defaultInstanceExtended.testFunc();
Extending the defaultClass
using a constructor function:
I myself prefer to use the object method above, but what if we don't have an object to inherit from? Well the standard way of inheriting in JavaScript is to define a constructor function and call new on it. Well we can do the same thing with jsExtender
and it will automatically add the extend
function for us.
// The constructor function that will inherit from defaultClass.
function ExtensionConstructor () {
console.log('The default constructor has been overridden by the ExtensionConstructor!');
}
// This prototyped function will be available after the inheritance occurs.
ExtensionConstructor.prototype.myPrototypedFunction = function (x, y) {
console.log('x: ' + x);
console.log('y: ' + y);
};
// Make ExtensionConstructor inherit from the default class.
var extensionFunctionClass = defaultClass.extend(ExtensionConstructor);
// Create a new instance of the extended default class.
var extensionFunctionInstance = new extensionFunctionClass();
// The function declared on the ExtensionConstructor should still work.
extensionFunctionInstance.myPrototypedFunction(20, 50);
// The testFunc declared on defaultClass should still work in extensionFunctionInstance.
extensionFunctionInstance.testFunc();
Calling the base
function of an inherited class:
Last and most important. The ability to call the base function from any overridden function within the inheritance chain. Without this inheritance is practically useless and multiple inheritance is impossible unless you specifically call the prototype of the parent class. There are some implementations that use this.parent
or this.super
. Most of those implementations will only work with single inheritance and not multiple inheritance since this.anything
will be overwritten the next time you try to inherit that same class which breaks the inheritance chain.
var baseTestClass = extensionFunctionClass.extend({
constructor: function() {
console.log('Constructor from baseTestClass has been called.');
this.base.call(this);
},
testFunc: function() {
console.log('The test function has been called from the baseTestClass.');
this.base.call(this);
},
testFunc2: function() {
console.log('Testing testFunc2 from baseTestClass.');
},
myPrototypedFunction: function(x, y, z) {
this.base.call(this, x, y);
console.log('z: ' + z);
}
});
// Make an instance of the baseTestClass. It should call
// it's own constructor and the constructor of it's parent.
var baseTestInstance = new baseTestClass();
// Make sure all other calls still work as expected.
// They should also call the base functions.
baseTestInstance.testFunc();
baseTestInstance.testFunc2();
baseTestInstance.myPrototypedFunction(10, 20, 30);
// Multiple Inheritance with base calls.
var baseTestClass2 = baseTestClass.extend({
constructor: function() {
console.log('Constructor from baseTestClass2 has been called.');
this.base.call(this);
},
testFunc: function() {
console.log('The test function has been called from the baseTestClass2.');
this.base.call(this);
},
testFunc2: function() {
this.base.call(this);
console.log('Testing testFunc2 from baseTestClass2.');
},
myPrototypedFunction: function(x, y, z) {
console.log('myPrototypedFunction has been called from baseTestClass2.');
this.base.call(this, x, y, z);
}
});
// Make an instance of the baseTestClass2. It should call
// it's own constructor and the constructor of it's parent.
var baseTestInstance2 = new baseTestClass2();
// Make sure all other calls still work as expected.
// They should also call the base functions.
baseTestInstance2.testFunc();
baseTestInstance2.testFunc2();
baseTestInstance2.myPrototypedFunction(10, 10, 10);
Creating instances without using new
:
For ease of use I decided to add another feature to jsExtender which will allow the creation of objects without using the new
keyword. I prefer to not use the it since it can easily be forgotten and I'm used to writing javascript modules that return new instances of objects for me.
// Create the class definition.
var personClass = jsExtender({
constructor: function(name) {
this.name = name;
},
displayName: function() {
console.log('My name is: ' + this.name);
}
});
// Creating an instance of personClass without new.
var myObject = personClass.create('Bob');
myObject.displayName();
And that's it.
Points of Interest
This took me some time to get right with many failed attempts. Javascript inheritance is not easy especially when trying to make it cleaner and easier to use. The most difficult part was getting the proper constructor's working on inherited objects and allowing the base functions to be accessed without overwriting the next parent's function in the chain.
With that being said, I'm sure there are bugs and issues with this implementation so if you find any feel free to let me know in the comments or submit an issue on GitHub (https://github.com/dredmond/jsExtender).
History
- 07/18/2014 - Article Published
- 07/31/2014 - Added an example for the create method.