Monday, June 3, 2024
 Popular · Latest · Hot · Upcoming
7
rated 0 times [  10] [ 3]  / answers: 1 / hits: 21845  / 11 Years ago, mon, may 20, 2013, 12:00:00

I have wondered for a couple years now what people think of doing inheritance with a module-pattern-esque constructor pattern and WITHOUT normal prototypal inheritance. Why do programmers not use a module pattern for non-singleton js classes? For me the advantages are:




  • Very clear public and private scope (easy to understand the code and the api)

  • No needing to track the 'this' pointer via $.proxy(fn, this) in callbacks

  • No more var that = this, etc. with event handlers, etc. Whenever I see a 'this', I know it is context that is being passed into a callback, it is NOT something I am tracking to know my object instance.



Disadvantages:




  • Small perf degradation

  • Risk possible wag of the finger from Doug Crockford?



Consider this (just run in any js console)



var Animal = function () {
var publicApi = {
Name: 'Generic',
IsAnimal: true,
AnimalHello: animalHello,
GetHelloCount:getHelloCount
};

var helloCount = 0;

function animalHello() {
helloCount++;
console.log(publicApi.Name + ' says hello (animalHello)');
}

function getHelloCount(callback) {
callback.call(helloCount);
}

return publicApi;
};

var Sheep = function (name) {
var publicApi = {
Name: name || 'Woolie',
IsSheep: true,
SheepHello: sheepHello
};

function sheepHello() {
publicApi.AnimalHello();
publicApi.GetHelloCount(function() {
console.log('i (' + publicApi.Name + ') have said hello ' + this + ' times (sheepHello anon callback)');
});
}

publicApi = $.extend(new Animal(), publicApi);
return publicApi;
};

var sheepie = new Sheep('Sheepie');
var lambie = new Sheep('Lambie');

sheepie.AnimalHello();
sheepie.SheepHello();
lambie.SheepHello();


My question is what are the drawbacks to this approach that I am not seeing? Is this a good approach?



Thanks!



[update]



Thanks for the great responses. Wish I could give everyone the bounty. It was what I was looking for. Basically what I thought. I would never use module pattern to construct more than a few instances of something. Usually only a couple. The reason I think it has its advantages is whatever small perf degradation you see is recaptured in the simplicity of the coding experience. We have a LOT of code to write these days. We also have to reuse other peoples' code and personally I appreciate when someone has taken the time to create a nice elegant pattern rather than dogmatically adhering to prototypal inheritance when it makes sense.


More From » jquery

 Answers
70

I think it boils down to the issue of performance. You mentioned that there is small performance degradation, but this really depends on scale of the application(2 sheep vs 1000 sheep). Prototypal inheritance should not be ignored and we can create an effective module pattern using a mix of functional and prototypal inheritance.



As mentioned in the post JS - Why use Prototype?,
one of the beauties of prototype is that you only need to initialize the prototypal members only once,
whereas members within the constructor are created for each instance
.
In fact, you can access prototype directly without creating a new object.



Array.prototype.reverse.call([1,2,3,4]);
//=> [4,3,2,1]

function add() {
//convert arguments into array
var arr = Array.prototype.slice.call(arguments),
sum = 0;
for(var i = 0; i < arr.length; i++) {
sum += arr[i];
}

return sum;
}

add(1,2,3,4,5);
//=> 15


In your functions, there is extra overhead to create a completely new
Animal and sheep each time a constructor is invoked. Some members such as Animal.name are created with each instance, but we know
that Animal.name is static so it would be better to instantiate it once. Since your code implies that Animal.name
should be the same across all animals, it is easy to update Animal.name for all instance simply by updating Animal.prototype.name if we moved
it to the prototype.



Consider this



var animals = [];
for(var i = 0; i < 1000; i++) {
animals.push(new Animal());
}


Functional inheritance/Module Pattern



function Animal() {

return {
name : 'Generic',
updateName : function(name) {
this.name = name;
}
}

}


//update all animal names which should be the same
for(var i = 0;i < animals.length; i++) {
animals[i].updateName('NewName'); //1000 invocations !
}


vs. Prototype



Animal.prototype = {
name: 'Generic',
updateName : function(name) {
this.name = name
};
//update all animal names which should be the same
Animal.prototype.updateName('NewName'); //executed only once :)


As shown above with your currently module pattern we lose effeciency in
updating properties that should be in common to all members.



If you are concered about visibility, I would use the same modular method you are currently using to encapsulate private members but also use
priviledged members for accessing these members should they need to be reached. Priviledged members are public members that provide an interface to access private variables. Finally add common members to the prototype.



Of course going this route, you will need to keep track of this.
It is true that in your implementation there is





  • No needing to track the 'this' pointer via $.proxy(fn, this) in callbacks


  • No more var that = this, etc. with event handlers, etc. Whenever I see a 'this', I know it is context that is being passed into a callback, it is NOT something I am tracking to know my object instance.



, but you are are creating a very large object each time which will consume more memory in comparison to using some prototypal inheritance.



Event Delegation as Analogy



An analogy to gaining performance by using prototypes is improved performance by using event delegation when manipulating the DOM.Event Delegation in Javascript



Lets say you have a large grocery list.Yum.



<ul =grocery-list> 
<li>Broccoli</li>
<li>Milk</li>
<li>Cheese</li>
<li>Oreos</li>
<li>Carrots</li>
<li>Beef</li>
<li>Chicken</li>
<li>Ice Cream</li>
<li>Pizza</li>
<li>Apple Pie</li>
</ul>


Let's say that you want to log the item you click on.
One implementation would be to attach an event handler to every item(bad), but if our list is very long there will be a lot of events to manage.



var list = document.getElementById('grocery-list'),
groceries = list.getElementsByTagName('LI');
//bad esp. when there are too many list elements
for(var i = 0; i < groceries.length; i++) {
groceries[i].onclick = function() {
console.log(this.innerHTML);
}
}


Another implementation would be to attach one event handler to the parent(good) and have that one parent handle all the clicks.
As you can see this is similar to using a prototype for common functionality and significantly improves performance



//one event handler to manage child elements
list.onclick = function(e) {
var target = e.target || e.srcElement;
if(target.tagName = 'LI') {
console.log(target.innerHTML);
}
}


Rewrite using Combination of Functional/Prototypal Inheritance



I think the combination of functional/prototypal inheritance can be written in an easy understandable manner.
I have rewritten your code using the techniques described above.



var Animal = function () {

var helloCount = 0;
var self = this;
//priviledge methods
this.AnimalHello = function() {
helloCount++;
console.log(self.Name + ' says hello (animalHello)');
};

this.GetHelloCount = function (callback) {
callback.call(null, helloCount);
}

};

Animal.prototype = {
Name: 'Generic',
IsAnimal: true
};

var Sheep = function (name) {

var sheep = new Animal();
//use parasitic inheritance to extend sheep
//http://www.crockford.com/javascript/inheritance.html
sheep.Name = name || 'Woolie'
sheep.SheepHello = function() {
this.AnimalHello();
var self = this;
this.GetHelloCount(function(count) {
console.log('i (' + self.Name + ') have said hello ' + count + ' times (sheepHello anon callback)');
});
}

return sheep;

};

Sheep.prototype = new Animal();
Sheep.prototype.isSheep = true;

var sheepie = new Sheep('Sheepie');
var lambie = new Sheep('Lambie');

sheepie.AnimalHello();
sheepie.SheepHello();
lambie.SheepHello();


Conclusion



The takeaway is to use both prototypal and functional inheritance to their advantages both to tackle performance and visibility issues.
Lastly, if you are working on a small JavaScript applications and these performance issues are not a concern,
then your method would be viable approach.


[#78117] Sunday, May 19, 2013, 11 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
alyssiat

Total Points: 608
Total Questions: 102
Total Answers: 101

Location: Japan
Member since Sat, Jun 6, 2020
4 Years ago
alyssiat questions
;