Let's actually compare both the ways and see which one is faster: http://jsperf.com/traditional-oop-vs-derek-s-oop-variant
As you can see your method is much slower than the traditional method. The reasons are:
- Your constructor is doing more stuff than required. Hence if you create multiple instances then the extra cost of creating an instance adds up.
- As @Alxandr mentioned you're creating a new anonymous function for
method
every time you create a new instance for no good reason. It'll only be useful once after which it'll be a waste of processing power.
- You're calling a function to check whether the
prototype
of the constructor has a given method or not, and to add the method to the prototype
if it doesn't. This seems unnecessary. You don't need to create a function to do this for you. IMHO the function call is just additional overhead.
Since you asked for criticism:
I'm aware that augmenting Object.prototype
isn't considered a good practice. But other than that are there any downsides with this approach?
Beside being terribly slow your approach also suffers from:
- Being difficult to understand. You may find this approach intuitive. However a person reading your code would surely wonder what the function
this.method
does. They would need to read the definition of Object.prototype.method
to fully comprehend your code.
- Being unintuitive. As I mentioned before it makes no sense to be defining
prototype
properties inside the constructor. It'll only be needed once after which it'll just become additional baggage. It's best to keep the constructor logic and the prototype
properties separate.
- It may lead to unexpected behavior. As @basilikum pointed out if you never call the constructor then the
prototype
properties will never be set. This may lead to problems when you attempt to access a property on the prototype
. For example, when inheriting properties from the prototype
no properties will be inherited until the base constructor is called.
I believe your goal is to encapsulate both the constructor and the prototype
properties into a single entity:
However I was trying to come up with a way to define a function on a prototype without separating the function definition with the constructor.
Is there an easy way to do this? Let's see, JavaScript is a prototypal object oriented language. Hence we should focus more on the prototype
instead of the constructor.
The above diagram was taken from the following answer: https://stackoverflow.com/a/8096017/783743
This diagram shows us:
- Every constructor has a property called
prototype
which points to the prototype object of the constructor function.
- Every prototype has a property called
constructor
which points to the constructor function of the prototype object.
- We create an instance from a constructor function. However the instance actually inherits from the
prototype
, not the constructor.
This is very useful information. Traditionally we've always created a constructor function first and then we've set its prototype
properties. However this information shows us that we may create a prototype object first and then define the constructor
property on it instead.
For example, traditionally we may write:
function Shape() {
this.x = 0;
this.y = 0;
}
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
};
However using our newfound knowledge we may write the same thing as:
var shape = {
constructor: function () {
this.x = 0;
this.y = 0;
},
move: function (x, y) {
this.x += x;
this.y += y;
}
};
The information contained in both these examples is the same. However we need a little additional scaffolding to make the second example work. In particular we need to do:
var Shape = shape.constructor;
Shape.prototype = shape;
This is not a big issue. We simply create a function to do this for us:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
Now we can define Shape
as follows:
var Shape = defclass({
constructor: function () {
this.x = 0;
this.y = 0;
},
move: function (x, y) {
this.x += x;
this.y += y;
}
});
As you can see encapsulation is easy to achieve in JavaScript. All you need to do is think sideways. Inheritance however is a different issue. You need to do a little more work to achieve inheritance.
Hope this helped.