views:

458

answers:

3

Could you explain the difference between setting methods in the constructor and through prototype object? The following code shows these two ways of setting the methods - say_hello and say_bye both work fine:

function MessageClass() {
  this.say_bye = function() { alert('see ya'); };
}

MessageClass.prototype.say_hello = function() { alert('hello'); };

x = new MessageClass();
x.say_hello();
x.say_bye();
+5  A: 

The difference is when you derive a class from Message Class. Only the methods declared on the prototype will be available on child classes of Message.

foxxtrot
So if I had 'BetterMessageClass.prototype = new MessageClass' only 'say_hello' would be inherited?
snitko
@Fox: I am able to to call the "say_bye" method (which is declared as "this") from the child class. Here is my code: function Employee() { this.say_bye = function () { alert('see ya'); }; } function Manager() { } Manager.prototype = new Employee; Manager.prototype.constructor = Manager; var manager = new Manager(); manager.say_bye();
Raghav Khunger
+4  A: 

If you bind methods by prototype JS only has to do it once and binds to an object class (which makes it elligible for OO JS extensions).

If you do the binding within the "class" function, JS has to do the work of creating and assigning for each and every instance.

annakata
Is this a performance issue?
Dana Robinson
yes, most definitely
annakata
What if I define methods as funcations first, then say the following in the constructor:'this.say_bye = MessageClass_sayhello;
snitko
slightly better, but still not as efficient as prototyping and you still can't extend on that
annakata
The question of being able to extend an object will depend largely on which js lib you are using and if you are using one. If the constructor gets called when the object is extended then the methods are available. I believe that most major libraries do call the constructor when you extend.
Prestaul
For example, if I extend an object myself: Subclass.prototype = new Class(); then the functions assigned in the Class constructor are available on Subclass. However, those functions are shared across instances of Subclass which could lead to unexpected behavior. Certainly better to prototype them.
Prestaul
+12  A: 

foxxtrot and annakata are both correct, but I'll throw in my 2 cents.

If you use the prototype then each instance of the "MessageClass" is really referencing the same functions. The functions exist in memory only once and are used for all instances. If you declare the methods in the constructor (or otherwise add it to a specific instance) rather than the prototype then a new function is created for each instance of MessageClass.

That being said, there is probably not any noticeable performance difference for most cases and it is unlikely that you will see a memory usage difference either. I would go with the prototype method unless you have a compelling reason to do otherwise. The only reason I can thing that you might want to declare a method in the constructor is if you need a closure. For example, if you have event handlers or you wanted to simulate private properties with getters/setters you might do:

function MessageClass() {
    var self = this;
    this.clickHander = function(e) { self.someoneClickedMe = true; };

    var _private = 0;
    this.getPrivate = function() { return _private; };
    this.setPrivate = function(val) { _private = val; };
}

EDIT: Because there has been discussion about how this effects objects extended by another object with functions assigned in the constructor I'm adding a bit more detail. I might use the term "class" to simplify the discussion, but it is important to note that js does not support classes (that doesn't mean we can't do good OO development) or we would not be discussing this issue.

Most javascript libraries call the constructor on the base class and the sub class. (e.g. Prototype.js's Object.extend) This means that methods assigned in the constructor of each will be available on the resulting objects. However, if you are extending objects yourself there can be unexpected consequences.

If I take the MessageClass above and extend it:

function ErrorMessageClass() {}
ErrorMessageClass.prototype = new MessageClass();

errorMsg = new ErrorMessageClass();

Then errorMsg will have a getPrivate and setPrivate method on it, but they may not behave as you would expect. Because those functions were scoped when they were assigned (i.e. at "ErrorMessageClass.prototype = new MessageClass()" not only are the get/setPrivate methods shared, the _private variable gets shared across all instances of ErrorMessageClass as well. This essentially makes _private a static property for ErrorMessageClass. For example:

var errorA = new ErrorMessageClass();
var errorB = new ErrorMessageClass();
errorA.setPrivate('A');
console.log(errorA.getPrivate()); // prints 'A'
console.log(errorB.getPrivate()); // prints 'A'
errorB.setPrivate('B');
console.log(errorA.getPrivate()); // prints 'B'

Likewise with the clickHandler function and someoneClickedMe property:

errorA.clickHandler();
console.log(errorA.someoneClickedMe); // prints 'true'
console.log(errorB.someoneClickedMe); // prints 'true'

However, change those function definitions to use this._private:

this.getPrivate = function() { return this._private; };
this.setPrivate = function(val) { this._private = val; };

and behavior of instances of ErrorMessageClass becomes more of what you would expect:

errorA.setPrivate('A');
errorB.setPrivate('B');
console.log(errorA.getPrivate()); // prints 'A'
console.log(errorB.getPrivate()); // prints 'B'
Prestaul
damn you for your eloquence :) (though I disagree there isn't a noticeable difference)
annakata
Though if you do use this._private, the variable is accessible from the outside.
Chris
(technically speaking, you can trace in with a debugger either way in most, if not all, JS environments)
Chris