If you really want to create your own inheritance pattern then John Resig's Simple Inheritance pattern is not a good candidate to build upon. The reason is because John Resig's code is very slow. The reason it's so slow is because of the way _super
is handled:
// 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];
}
I'll try to explain what's happening in the above code:
- We loop through each property which we wish to copy onto the newly extended object.
- If the property is a function and it overrides another function of the same name and it needs to call the overridden function then we replace it with a function which changes the value of
this._super
within the function to the overridden function for that particular function call.
- Otherwise we simply copy the property as it is.
This little indirection allows you to call overridden methods using this._super
. However it also makes the code very slow. Hence I suggest you don't use John Resig's Simple JavaScript Inheritance.
Since this is a code review site I'm also obliged to point out flaws in your code. The main problem with your code is in the Class.create
function:
Class.create = function () {
var instance = new this();
if (instance.init) {
instance.init();
}
return instance;
};
Compare that with John Resig's original dummy class constructor:
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
The problem is that any argument applied to Class.create
is lost. You need to apply
the arguments
passed to Class.create
to instance.init
as follows:
Class.create = function () {
var instance = new this;
if (instance.init) instance.init.apply(instance, arguments);
return instance;
};
Beside that there's not much more scope for improvement if you plan to stick to John Resig's Simple Inheritance Pattern.
If you really want to create your own inheritance pattern in JavaScript then it pays to invest some time learning about how inheritance works in JavaScript. For example the following answer explains prototype-class isomorphism in JavaScript: https://stackoverflow.com/a/17893663/783743
Prototype-class isomorphism simply means that prototypes can be used to model classes. Armed with this knowledge we can write a function which takes an object (a prototype) and returns a class (a constructor function):
function CLASS(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
Using the above function we may now create and instantiate classes as follows:
var Person = CLASS({
constructor: function (isDancing) {
this.dancing = isDancing;
},
dance: function () {
return this.dancing;
}
});
var p = new Person(true);
Although this pattern does not have inheritance yet it is a good base to build upon. With a little bit of modification we can make it behave the way we want it to. There are a few important points to note:
- We want to be able to use the
extend
and create
functions on any function. Hence it's better to add them to Function.prototype
instead of creating a separate Class
constructor.
- For a function
F
it need not be true that F.prototype.constructor === F
. Hence we may use F
for extending and F.prototype.constructor
for creating.
- Instead of passing a prototype to
extend
it makes much more sense to pass a blueprint of a prototype instead. This makes looping over the prototype unnecessary.
Taking advantage of the above mentioned points we can implement extend
and create
as follows:
Function.prototype.extend = function (body) {
var constructor = function () {};
var prototype = constructor.prototype = new this;
body.call(prototype, this.prototype);
return constructor;
};
Function.prototype.create = function () {
var instance = new this;
instance.constructor.apply(instance, arguments);
return instance;
};
That's all. Using two simple functions we may now create classes as follows:
var Person = Object.extend(function () {
this.constructor = function (isDancing) {
this.dancing = isDancing;
};
this.dance = function () {
return this.dancing;
};
});
var Ninja = Person.extend(function (base) {
this.constructor = function () {
base.constructor.call(this, false);
};
this.swingSword = function () {
return true;
};
});
To create an instance of the class we use create
as follows:
var p = Person.create(true);
p.dance(); // => true
var n = Ninja.create();
n.dance(); // => false
n.swingSword(); // => true
// Should all be true
p instanceof Person && p instanceof Object &&
n instanceof Ninja && n instanceof Person && n instanceof Object
This code is similar to my very own augment
function. However augment
makes use of new
instead of create
. Because of the way JavaScript works, using new
is the fastest way to create an instance which is why augment
doesn't have create
. Nevertheless if you want it, a functional version of new
can be easily implemented as follows:
function Factory(constructor, args) {
return constructor.apply(this, args);
}
Function.prototype.new = function () {
Factory.prototype = this.prototype;
return new Factory(constructor, args);
};
For more information read the following answer on StackOverflow.