Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / Javascript

JavaScript Extension / Inheritance: jsExtender

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
1 Aug 2014CPOL3 min read 15.2K   6   5
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.

JavaScript
// 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.

JavaScript
// 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.

JavaScript
// 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.

JavaScript
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.

JavaScript
// 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHave you looked at using real classes? Pin
Dewey20-Jul-14 21:03
Dewey20-Jul-14 21:03 
AnswerRe: Have you looked at using real classes? Pin
Donny Redmond21-Jul-14 4:34
Donny Redmond21-Jul-14 4:34 
GeneralRe: Have you looked at using real classes? Pin
Dewey29-Jul-14 19:55
Dewey29-Jul-14 19:55 
GeneralRe: Have you looked at using real classes? Pin
Donny Redmond30-Jul-14 2:36
Donny Redmond30-Jul-14 2:36 
GeneralRe: Have you looked at using real classes? Pin
Dewey7-Aug-14 20:28
Dewey7-Aug-14 20:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.