18

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();
0

3 Answers 3

31

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'
Sign up to request clarification or add additional context in comments.

3 Comments

damn you for your eloquence :) (though I disagree there isn't a noticeable difference)
Though if you do use this._private, the variable is accessible from the outside.
(technically speaking, you can trace in with a debugger either way in most, if not all, JS environments)
6

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.

4 Comments

What if I define methods as funcations first, then say the following in the constructor: 'this.say_bye = MessageClass_sayhello;
slightly better, but still not as efficient as prototyping and you still can't extend on that
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.
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.
5

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.

4 Comments

So if I had 'BetterMessageClass.prototype = new MessageClass' only 'say_hello' would be inherited?
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();
@foxxtrot That is not correct. Even methods declared on this inside constructor are accessible in a child class. Consider the code errorA.setPrivate('A') by Prestaul, which is accessing a method declared on this by the parent class MessageClass.
I was also mistakenly thinking the difference lies in inheritance, but it doesn't: function parentClass() { this.inheritedFunc = function() {return "Some Value"}; } function childClass() {}; childClass.prototype = new parentClass(); var childObj = new childClass(); console.log(childObj.inheritedFunc()); //successfully logs "Some Value"

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.