What are the downsides of defining functions on prototype this way?
function, javascript, prototype
Solution
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.
Problem
Usually people write code like this: ``` function Shape() { this.x = 0; this.y = 0; } Shape.prototype.move = function(x, y) { this.x += x; this.y += y; } ``` 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. Here is what I got: ``` Object.prototype.method = function(name, func) { if (typeof(this.constructor.prototype[name]) === 'undefined') { this.constructor.prototype[name] = func } } ``` This allows you to do something like: ``` function Shape() { this.x = 0; this.y = 0; this.method('move', function(x, y) { this.x += x; this.y += y; }) } ``` And also no matter how many times you create a shape, the function will only be defined once. I'm aware that augmenting `Object.prototype` isn't considered a good practice. But other than that are there any downsides with this approach? EDIT: Johan brought up a good point; I should have made `method` not enumerable. Here is the revised version: ``` Object.defineProperty(Object.prototype, 'method', { value: function(name, func) { if (typeof(this.constructor.prototype[name]) === 'undefined') { this.constructor.prototype[name] = func } }, enumerable: false }) ```