1

Let's say I have the following simple OOP-like implementation:

Implementation 1:

function Base() {
    this.text = "lol";
}

Base.prototype.hello = function() {
    console.log("Hello from base");
}

function Child() {
}

Child.prototype = new Base(); // All objects created from Child function now have reference to this Base object in their __proto__ property.

Child.prototype.hello = function() { // Changes the hello implementation in the prototype object
    console.log("Hello from child");
}

var child1 = new Child(); 
child1.hello(); // "Hello from child"

This is the implementation I see more often: The methods that are supposed to be inherited (like the hello method) from the Base object to child objects are defined using the prototype property.

However, if I bind the hello method right in to the base object using this keyword I get the same result:

Implementation 2:

function Base() {
    this.text = "lol";
    this.hello = function() {
        console.log("Hello from base");
    }
}

function Child() {
}

Child.prototype = new Base(); // All objects created from Child function now have reference to this Base object in their __proto__ property.

Child.prototype.hello = function() { // Changes the hello implementation in the prototype object
    console.log("Hello from child");
}

var child1 = new Child();
child1.hello(); // "Hello from child"

What's the difference between these two implementations?

Jarzka
  • 755
  • 7
  • 22
  • 1
    Well as you've seen, there's no difference at all in the behavior with respect to the overridden function. Other than that, the difference is that the base object directly has the "hello" property instead of its prototype, so if there were no override then the search for the property would involve one extra step. – Pointy Jul 25 '15 at 16:45
  • So if I don't use the prototype property in the base class, all methods in the Base object get copied if I create a different Child object that inherits from a new instance of the Base class. Is this correct? – Jarzka Jul 25 '15 at 16:50
  • 1
    Please consider setting up your inheritance using `Child.prototype = Object.create(Base.prototype);` and then `call`ing or `apply`ing `Base` on `this` in `Child` -- if you use this method you'll also see your code no longer behaves as you expect in implementation 2 due to the method being set on the child instance – Paul S. Jul 25 '15 at 16:50
  • Paul S: Is there a reason to use Object.create rather than the more simple new keyword? – Jarzka Jul 25 '15 at 16:52
  • 2
    @Jarzka `Object.create` doesn't invoke the constructor, meaning rather than _Child_ `child` inheriting an instance of _Base_ you inherit _Base's_ prototype chain with a clean _Object_ and you can choose how to construct `child`, i.e. (look at your console) http://jsfiddle.net/gksz497t/ – Paul S. Jul 25 '15 at 17:13
  • 1
    @Jarzka: Yes, many, see https://stackoverflow.com/questions/12592913/what-is-the-reason-to-use-the-new-keyword-here and http://stackoverflow.com/a/17393153/1048572/Benefits-of-using-Object.create-for-inheritance. One of them is to not get the behaviour from your question :-) – Bergi Jul 25 '15 at 19:07
  • 1
    @Bergi This question just asks the difference between setting `hello` in `Base.prototype` vs in every `Base` instance. I think [Why declare properties on the prototype for instance variables](http://stackoverflow.com/q/16751230/1529630) explains the difference. – Oriol Jul 25 '15 at 22:15
  • @Oriol: I really don't think so. That supposed duplicate asks about the `MyCircle.prototype.radius;` "declaration", and our canonical duplicate about where to define methods would be http://stackoverflow.com/q/310870/1048572. But this (Jarzkas) question asks how defining a method in the constructor or a prototype works together with (wrong) inheritance in a `Child` class that may attempt to overwrite the definition of the `Parent`. – Bergi Jul 26 '15 at 12:33

2 Answers2

3

Prototype methods are shared by all the objects under the same class while adding a method to the object itself makes the function private to that specific object.

If you are creating only one object from the class, there is no difference in performance. However, if you are creating more than one object, prototype method will improve performance significantly.

Here is an example

function Base() {
    this.text = "lol";
}

Base.prototype.hello = function() {
    console.log("Hello from base");
}

var obj1 = new Base();
var obj2 = new Base();
obj1.hello();
obj2.hello();

Base.prototype.hello = function() {
    console.log("Bye");
}

obj1.hello();
obj2.hello();

function Base() {
    this.text = "lol";
    this.hello = function() {
        console.log("Hello from base");
    };
}

var obj1 = new Base();
var obj2 = new Base();
obj1.hello();
obj2.hello();

obj1.hello = function() {
    console.log("Bye");
}

obj1.hello();
obj2.hello();
NoGray
  • 1,149
  • 7
  • 9
2

Basically you are asking: What is the difference between

function Base() {
    this.text = "lol";
}

Base.prototype.hello = function() {
    console.log("Hello from base");
}

and

function Base() {
    this.text = "lol";
    this.hello = function() {
        console.log("Hello from base");
    }
}

Functionally, there is none.

But: In the latter setup every Base instance contains a separate copy of the hello method.

That means

  • Two object instances will be physically larger than two object instances of the former setup.
  • With the former setup you can override a method for all live instances in a single step (imagine debugging a large object graph): You simply swap it on the prototype.
  • With the latter setup you would have to swap out the method on every live instance individually.
  • Methods in the former setup don't have access to instance variables (those declared with var inside the constructor).
  • Methods in the latter setup do.
  • Methods in the prototype setup are inherently public.

Personally I find the prototype setup cleaner: It separates functionality from object instances. It saves memory. It forces you to use sensible scoping.

If you need a function to have access to private instance data (that you do not under any circumstances want to assign to this), then adding that method in the constructor is the only way to do it.

But for all commonly shared functionality I recommend using the prototype.

Tomalak
  • 332,285
  • 67
  • 532
  • 628