9

A prototype is used to declare properties and methods for a class of objects. One advantage of using prototype is that it conserves memory because all instances of a class point to the properties and methods of the prototype which conserves memory and effectively allows properties to be treated as static by all instances of a class.

Prototype is used for inheritance through prototype chaining.

My question is very simple. Why use prototype at all when you can just do:

function car() {
    this.engine = "v8";
}
function mustang() {
    // nm, no good way to inherit without using prototypes
}

Is that right? So the primary purpose of prototypes is threefold:

  1. conserve memory
  2. provide static properties
  3. is the only way for a reference type to inherit from a super class
user1472219
  • 199
  • 5
  • 9
  • 1
    I'm not sure what you're asking... You seem to say "you can just do ...", but then in the comment in that script you say "no good way to [do it]". Doesn't that contradict itself? – Joeytje50 Jan 23 '14 at 19:10
  • 1
    I think he realized his thoughts were mistaken partway through asking the question. At any rate he hasn't been on stackexchange since he posted the question 18 months ago, so no point addressing him. It was me who's still wondering... – temporary_user_name Jan 24 '14 at 03:11
  • if you don't use prototype you can do function mustang() { car.apply(this); } – Serge Jan 28 '14 at 10:45

6 Answers6

2

conserve memory

Yes it does, when you create hundreds of instances of Car and they all have their own functions (that have their own closure scopes) you'll consume more memory.

Can't find a reference for it but it has been suggested that Chrome optimises constructor functions that use prototype better than constructor functions with everything in the constructor body.

provide static properties

Static is more like Date.now(), every instance has members from the prototype but can be called on the instance.

is the only way for a reference type to inherit from a super class

You can inherit with Parent.apply(this,arguments); in Child but it makes extending Parent functions more complicated and doesn't make childInstance instanceof Parent true. What that code does is run Parent code with the to be created Child instance as the invoking object (this). Inheritance is usually done in 2 places.

  1. In the Child body Parent.apply(this,arguments); to re use Parent initialisation code and make Parent instance members to be Child instance members (for example: this.name).
  2. Setting Child.prototype to shallow copy of Parent.prototype Child.prototype=Object.create(Parent.prototype);Child.prototype.constructor=Child; This will ensure that shared Parent members are available on Child instances (like the function getName).

These points are explained in more detail here: https://stackoverflow.com/a/16063711/1641941

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
1

Concerning your three points:

  1. Prototypes aren't necessarily more performant, especially for prototype chains that become long or contain many members. The smaller the prototype and shorter the chain, the more the browser's compiler can optimize it. Ultimately, this question needs to be asked for individual applications, their individual needs, and the browsers being targeted (which can vary widely in performance).
  2. By definition, static members require objects. That is, static members belong to an object itself rather than to a specific instance. Objects are the only way to create static properties in JavaScript. Note that object literals, which are a "special" kind of Object, are essentially static.
  3. One could implement his own type of object that would allow for something like inheritance (i.e., jQuery.extend), but as far as reference types go, prototypes are the only way to create inheritance.
Donald T
  • 10,234
  • 17
  • 63
  • 91
  • Doesn't that jspref show that prototype out performs the others? Only Chrome 29 and 30 are about the same. This is about operations per second and doesn't show memory consumption. In point 3; the only way to have `someEmployer instanceof Person` to be true is to use prototype – HMR Jan 24 '14 at 02:25
  • HMR, thanks for catching that -- it was a poor example on my part. I edited the answer to explain the reason instead. – Donald T Jan 24 '14 at 03:02
  • Yes, there could be a negative impact on performance https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain#Performance in a loop that invokes methods high up the prototype chain you may notice this. – HMR Jan 24 '14 at 03:05
0

Prototyping is a lot more than that. You can also extend classes and already existing instances of objects with methods and properties during runtime.

This should explain it in a very understandable way: http://javascript.info/tutorial/inheritance

drw85
  • 184
  • 6
0

If you care about following conventions so that people (and you down the road) actually understand your code, you shouldn't put this.engine="v8" in the constructor. Prototype is meant to define properties for every single car, and the constructor is meant to define individual instances. So why would you put something that is true for every instance smack dab in the constructor? This belongs in the prototype. There's something to be said for putting things in their proper place even if doing both things end up doing accomplishing the same thing. Your code will be understandable by you and others.

Nick Manning
  • 2,828
  • 1
  • 29
  • 50
0

Regarding your points:

  1. There is definitely a performance boost, especially regarding functions - it's much better to declare functions on the prototype.
  2. I think you meant to say "public" properties so that information is retrieved by writing some_instance.foo. "Static" properties/methods are different (see below).
  3. Correct. Inheritance can only really happen from the prototype.

Let me explain some things to see if this helps. Creating new "classes" in javascript is a fairly straightforward process.

var MyClass = new Function();

At this point, the engine is aware of your new class and knows "what to do" (in terms of performance) when it creates new instances of your "class".

var my_instance = new MyClass();

If you want to modify the prototype, you can do so and know that every instance is going to get updated because they all share the same prototype.

MyClass.prototype.name = "default name";
console.log( my_instance.name ); //=> default name

Now the engine knows that there is a "name" property which expects a string value... it will allot those resources to all new AND existing instances of your class... which is very handy. Beware that modifying the prototype of an existing "class" like this is an expensive process and should not be performed frequently (but don't be afraid to do it either).

I can't really speak for the performance pros and cons of declaring ad-hoc properties/methods on an instance:

my_instance.foo = function() { /* this is not in the prototype chain */ };

My guess is that this is pretty simple for the engine and is no big deal unless you are doing this for tens of thousands of objects at the same time.

The main benefit of using the prototype IMO is that you can write code to extend a method's functionality and know that all instances of your "class" will get updated accordingly:

var old_foo = MyClass.prototype.foo;
MyClass.prototype.foo = function() {
    /* new business logic here */

    // now call the original method.
    old_foo.apply(this, arguments);
};

Regarding "static" properties, you declare those on the "class" (constructor) itself:

// example static property
MyClass.num_instances = 0;

Now you can create init/destroy methods like this:

MyClass.prototype.init = function() {
    this.constructor.num_instances++;
};

MyClass.prototype.destroy = function() {
    this.constructor.num_instances--;
};

// and call the init method any time you create a new instance
my_instance.init();
console.log( MyClass.num_instances ); //=> 1

var instance_2 = new MyClass();
instance_2.init();
console.log( MyClass.num_instances ); //=> 2

instance_2.destroy();
console.log( MyClass.num_instances ); //=> 1

Hope that helps.

Ryan Wheale
  • 26,022
  • 8
  • 76
  • 96
0

(1) I don't think conserving memory alone is a valid reason to utilize .prototype, unless you're getting really extreme with duplicating objects.

(2) The idea of static properties is not really a reason to use .prototype either (IMHO), because it doesn't behave like a traditional static property. You (as far as I know) always need an object instance before you can access the "static" property, which makes it not static at all.

function Car() {}
Car.prototype.Engine = "V8";
// I can't access Car.Engine... I'll always need an instance.
alert(new Car().Engine);
// or
var car1 = new Car();
alert(car1.Engine); //you always need an instance.
//unless you wanted to do
alert(Car.prototype.Engine); //this is more like a static property, but has an
//unintended consequence that every instance of Car also receives a .Engine
//behavior, so don't do this just to create a "static property."

It should be noted that this "static" idea not only applies to properties but all members, which includes methods (functions), from a traditional OO perspective.

It is better to think about prototypes (again, IMHO) as injected singleton objects with behaviors that get attached to instance objects. All instances of Car() can have their own instance members, but every instance of Car() will also "automatically" be injected with all Car.prototype's members/behaviors. It's not technically the same, but I find it convenient to think about prototypes in that way.

//define Car and Car.GoFast
function Car() {}
Car.prototype.GoFast = function () { alert('vroom!'); };

var car1 = new Car();
var car2 = new Car();

car1.GoFast();
car2.GoFast(); //both call to same GoFast implementation on Car.prototype

//change the GoFast implementation
Car.prototype.GoFast = function () { alert('vvvvvrrrrroooooooooommmmm!!!!!'); };

car1.GoFast();
car2.GoFast(); //both have been "updated" with the new implementation because
//both car1 and car2 are pointing to the same (singleton) Car.prototype object!

Car.prototype is behaving like a singleton object whose members/behaviors have been injected into instance objects of type Car.

(3) Prototypes shouldn't be confused for inheritance. You can get behavior that appears to be inheritance, but it is not. The members/behaviors on the prototype remain on the prototype object. They do not become members/behaviors of your derived class, like true inheritance. That is why I describe it more like the prototype being "injected" into your instance.

function Car() {}
Car.prototype.Engine = "V8";
var car1 = new Car();
var car2 = new Car();

alert(car1.Engine); //alerts "V8"
//There is no "Engine" variable defined within the instance scope of 'car1'.
//Javascript searches the scope of car1's Type.prototype object to find 'Engine'.
//Equivalent to: Object.getPrototypeOf(car1).Engine

//But if we create 'Engine' within the scope of car1
car1.Engine = "V6"; //Car.prototype was never accessed or updated
alert(car1.Engine); //we get "V6"
alert(car2.Engine); //we still get "V8"
alert(Object.getPrototypeOf(car1).Engine); //we still get "V8"!

So to answer the question directly: Is there any benefit to using prototype instead of declaring properties on the object itself?

Yes, when you want to share behavior implementation amongst instance objects of a given Type. As a coincidence, you will reduce your memory footprint, but that is not a reason alone to use prototypes. Neither is "creating static properties" (which they're not ), or for inheritance (which it is not).

tyriker
  • 2,290
  • 22
  • 31