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.
var defaultClass = jsExtender();
defaultClass.prototype.testFunc = function () {
console.log('Testing the test function!');
};
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.
var defaultClassExtended = defaultClass.extend({
constructor: function () {
console.log('The default constructor has been overridden!');
}
});
defaultInstanceExtended = new 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.
function ExtensionConstructor () {
console.log('The default constructor has been overridden by the ExtensionConstructor!');
}
ExtensionConstructor.prototype.myPrototypedFunction = function (x, y) {
console.log('x: ' + x);
console.log('y: ' + y);
};
var extensionFunctionClass = defaultClass.extend(ExtensionConstructor);
var extensionFunctionInstance = new extensionFunctionClass();
extensionFunctionInstance.myPrototypedFunction(20, 50);
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);
}
});
var baseTestInstance = new baseTestClass();
baseTestInstance.testFunc();
baseTestInstance.testFunc2();
baseTestInstance.myPrototypedFunction(10, 20, 30);
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);
}
});
var baseTestInstance2 = new baseTestClass2();
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.
var personClass = jsExtender({
constructor: function(name) {
this.name = name;
},
displayName: function() {
console.log('My name is: ' + this.name);
}
});
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.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.