patternjavascriptMinor
Improving on John Resig's Simple JavaScript Inheritance: avoiding `new`
Viewed 0 times
simplenewjavascriptresiginheritancejohnavoidingimproving
Problem
I've recently read this article, and I agree that the
Thus, I've made an improvement on John Resig's Simple JavaScript Inheritance in order to use the
The initialization case in his article could be rewritten like this:
Have I done this well or not? Please help me check it.
new keyword is not good practice.Thus, I've made an improvement on John Resig's Simple JavaScript Inheritance in order to use the
Class.create method instead of new:// The dummy class constructor
function Class() {
// I remove the initialization procedure in constructor function,
// Initialization will done by Class.create which I defined below
}
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
// What I improved
Class.create = function () {
var instance = new this();
if (instance.init) {
instance.init();
}
return instance;
}The initialization case in his article could be rewritten like this:
var Person = Class.extend({
init: function(isDancing){
this.dancing = isDancing;
}
});
var p = Person.create();Have I done this well or not? Please help me check it.
Solution
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
I'll try to explain what's happening in the above code:
This little indirection allows you to call overridden methods using
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
Compare that with John Resig's original dummy class constructor:
The problem is that any argument applied to
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):
Using the above function we may now create and instantiate classes as follows:
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:
Taking advantage of the above mentioned points we can implement
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;
};
_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._superwithin 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
extendandcreatefunctions on any function. Hence it's better to add them toFunction.prototypeinstead of creating a separateClassconstructor.
- For a function
Fit need not be true thatF.prototype.constructor === F. Hence we may useFfor extending andF.prototype.constructorfor creating.
- Instead of passing a prototype to
extendit 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;
};
Code Snippets
// 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];
}Class.create = function () {
var instance = new this();
if (instance.init) {
instance.init();
}
return instance;
};// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}Class.create = function () {
var instance = new this;
if (instance.init) instance.init.apply(instance, arguments);
return instance;
};function CLASS(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}Context
StackExchange Code Review Q#30018, answer score: 2
Revisions (0)
No revisions yet.