Assigning Prototype Methods *Inside* the Constructor Function - Why Not

Assigning prototype methods *inside* the constructor function - why not?

Functionally, are there any drawbacks to structuring my code this way?
Will adding a prototypical method to a prototype object inside the
constructor function's body (i.e. before the constructor function's
expression statement closes) cause unexpected scoping issues?

Yes, there are drawbacks and unexpected scoping issues.

  1. Assigning the prototype over and over to a locally defined function, both repeats that assignment and creates a new function object each time. The earlier assignments will be garbage collected since they are no longer referenced, but it's unnecessary work in both runtime execution of the constructor and in terms of garbage collection compared to the second code block.

  2. There are unexpected scoping issues in some circumstances. See the Counter example at the end of my answer for an explicit example. If you refer to a local variable of the constructor from the prototype method, then your first example creates a potentially nasty bug in your code.

There are some other (more minor) differences. Your first scheme prohibits the use of the prototype outside the constructor as in:

Filter.prototype.checkProduct.apply(someFilterLikeObject, ...)

And, of course, if someone used:

Object.create(Filter.prototype) 

without running the Filter constructor, that would also create a different result which is probably not as likely since it's reasonable to expect that something that uses the Filter prototype should run the Filter constructor in order to achieve expected results.


From a run-time performance point of view (performance of calling methods on the object), you would be better off with this:

var Filter = function( category, value ){
this.category = category;
this.value = value;

// product is a JSON object
this.checkProduct = function( product ){
// run some checks
return is_match;
}

};

There are some Javascript "experts" who claim that the memory savings of using the prototype is no longer needed (I watched a video lecture about that a few days ago) so it's time to start using the better performance of methods directly on the object rather than the prototype. I don't know if I'm ready to advocate that myself yet, but it was an interesting point to think about.


The biggest disadvantage of your first method I can think of is that it's really, really easy to make a nasty programming mistake. If you happen to think you can take advantage of the fact that the prototype method can now see local variables of the constructor, you will quickly shoot yourself in the foot as soon as you have more than one instance of your object. Imagine this circumstance:

var Counter = function(initialValue){
var value = initialValue;

// product is a JSON object
Counter.prototype.get = function() {
return value++;
}

};

var c1 = new Counter(0);
var c2 = new Counter(10);
console.log(c1.get()); // outputs 10, should output 0

Demonstration of the problem: http://jsfiddle.net/jfriend00/c7natr3d/

This is because, while it looks like the get method forms a closure and has access to the instance variables that are local variables of the constructor, it doesn't work that way in practice. Because all instances share the same prototype object, each new instance of the Counter object creates a new instance of the get function (which has access to the constructor local variables of the just created instance) and assigns it to the prototype, so now all instances have a get method that accesses the local variables of the constructor of the last instance created. It's a programming disaster as this is likely never what was intended and could easily be a head scratcher to figure out what went wrong and why.

Adding prototype methods outside vs inside of constructor function

tl;dr: The prototype should be initialized outside the constructor.


The prototype object is something that should be initialized/created only once. Changing it inside the constructor means that every time a new instance is created, the prototype is changed one way or the other.

That kind of defeats the purpose of prototypes because they should be setup ones and shared across all instances (to "save" memory).

It's not so evident for Shape, but it becomes more evident for Circle:

function Shape() {    Shape.prototype.duplicate = function() {        console.log('Duplicate');    }}
function Circle() { Circle.prototype = Object.create(Shape.prototype);}
var c1 = new Circle();var c2 = new Circle();
console.log( Object.getPrototypeOf(c1) === Object.getPrototypeOf(c2), ':-O every Circle instance has its own prototype');
c1.duplicate();// can't even call `c1.duplicate` because // `Circle.prototype = Object.create(Shape.prototype);` happens // *after* the first instance was created

Defining prototype methods inside the constructor

This is a very bad idea, for a great number of reasons. A few of which are:

  1. Adding methods to the prototype in the constructor will cause the prototype method to be replaced for all instances, everytime you instantiate a new Dog.
  2. Calling Dog.prototype.bark() means that this will be Dog.prototype and not your instance of Dog, which can cause serious issues.
  3. this.bark = function () { Dog.prototype.bark(); } is some serious WTF. Because this.bark will already evaluate to the prototype method making this unnecessary. And calling it like this actually destroys the natural this value, as mentioned in #2.

Here is what is should be:

function Dog() {
this.makeSound();
};

Dog.prototype.bark = function() {
alert('woof');
};

Dog.prototype.makeSound = function() {
this.bark();
};

Or alternatively, without the prototype at all:

function Dog() {
this.bark = function() {
alert('woof');
};

this.makeSound = function() {
this.bark();
};

this.makeSound();
};

I would not trust this snippet of yours at all.

Functions inside constructor vs prototype

I perform a quick test. If you declare function in the constructor, two object instances have different function instances even after optimizations. However with prototype, you have only one instance of the function which explains the performance difference.

    var Person = function () {        var self = this;        self.firstName = null;        self.lastName = null;        self.fullName = function () {            return self.firstName + self.lastName;        };    };
Person.prototype.fullName2 = function () { return this.firstName + this.lastName; };
var a = new Person(); var b = new Person();
console.log(a.fullName == b.fullName); // returns false console.log(a.fullName2 == b.fullName2); // returns true

Assigning prototype inside constructor

prototype is a special property of the constructor function, not of the instance.

When you call the constructor function with new Func(), the engine will create a new object which inherits from Func.prototype and then sets this inside the constructor function to refer to the new object.

So, aside from this.prototype just being an ordinary property, the inheritance already took place when the assignment takes place.

Since you are not assigning any methods to MyClass.prototype, you don't have to do anything with prototype inheritance here. All you have to do is apply MyClass to the newly created instance using .call [MDN]:

var SecondClass = function(b) {
MyClass.call(this, b);
this.getB = function() {
return 6;
}
};

However, you should add all methods that are shared by instances to the prototype and then let each instance of SecondClass inherit from it. This is how a complete setup could look like:

var MyClass = function(b) {
this.a = b;
}
MyClass.prototype.getA = function() {
return this.a;
};

var SecondClass = function(b) {
// call parent constructor (like 'super()' in other languages)
MyClass.call(this, b);
}
// Setup inheritance - add 'MyClass.prototype' to the prototype chain
SecondClass.prototype = Object.create(MyClass.prototype);
SecondClass.prototype.getB = function() {
return 6;
};

var a = new SecondClass(2);
console.log(a.getA());

All of this will become easier in ES6.

Setting methods through prototype object or in constructor, difference?

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'

Why constructor function prototype can be assigned to an object and what it means

There are two different prototype chains at play:

  1. The prototype chain of the constructor
  2. The prototype chain of objects that are created by that constructor

They have nothing to do with each other. The first is a function and thus related to Function.prototype, the other is not a function, but an object whose prototype object is created at the same time as the constructor function object was created. That prototype object will be used when a new instance is created by calling the constructor with new.

What can be confusing here, is that the constructor has a property that is called prototype, but that is not the prototype object of the constructor (point 1), but of objects it can create (point 2). Possibly this property would have been better named prototypeForConstructedInstances... but that's a bit long ;-)

To get the constructors own prototype you would write Object.getPrototypeOf(constructor),... and you'll see it is Function.prototype.

You may have an interest in reading more about the broader subject of prototypes at "How does JavaScript .prototype work?". It's an interesting read, and you'll also find my answer over there.



Related Topics



Leave a reply



Submit