103

I'd like to understand when it is appropriate to use prototype methods in js. Should they always be used? Or are there cases where using them is not preferred and/or incurs a performance penalty?

In searching around this site on common methods for namespacing in js, it seems that most use a non-prototype based implementation: simply using an object or a function object to encapsulate a namespace.

Coming from a class-based language, it's hard not to try and draw parallels and think that prototypes are like "classes" and the namespace implementations I mentioned are like static methods.

opl
  • 1,147
  • 2
  • 9
  • 8

6 Answers6

143

Prototypes are an optimisation.

A great example of using them well is the jQuery library. Every time you obtain a jQuery object by using $('.someClass'), that object has dozens of "methods". The library could achieve that by returning an object:

return {
   show: function() { ... },
   hide: function() { ... },
   css: function() { ... },
   animate: function() { ... },
   // etc...
};

But that would mean that every jQuery object in memory would have dozens of named slots containing the same methods, over and over.

Instead, those methods are defined on a prototype and all jQuery objects "inherit" that prototype so as to gain all those methods at very little runtime cost.

One vitally important part of how jQuery gets it right is that this is hidden from the programmer. It's treated purely an optimisation, not as something that you have to worry about when using the library.

The problem with JavaScript is that naked constructor functions require the caller to remember to prefix them with new or otherwise they typically don't work. There is no good reason for this. jQuery gets it right by hiding that nonsense behind an ordinary function, $, so you don't have to care how the objects are implemented.

So that you can conveniently create an object with a specified prototype, ECMAScript 5 includes a standard function Object.create. A greatly simplified version of it would look like this:

Object.create = function(prototype) {
    var Type = function () {};
    Type.prototype = prototype;
    return new Type();
};

It just takes care of the pain of writing a constructor function and then calling it with new.

When would you avoid prototypes?

A useful comparison is with popular OO languages such as Java and C#. These support two kinds of inheritance:

  • interface inheritance, where you implement an interface such that the class provides its own unique implementation for every member of the interface.
  • implementation inheritance, where you extend a class that provides default implementations of some methods.

In JavaScript, prototypical inheritance is a kind of implementation inheritance. So in those situations where (in C# or Java) you would have derived from a base class to gain default behaviour, which you then make small modifications to via overrides, then in JavaScript, prototypical inheritance makes sense.

However, if you're in a situation where you would have used interfaces in C# or Java, then you don't need any particular language feature in JavaScript. There is no need to explicitly declare something that represents the interface, and no need to mark objects as "implementing" that interface:

var duck = {
    quack: function() { ... }
};

duck.quack(); // we're satisfied it's a duck!

In other words, if each "type" of object has its own definitions of the "methods", then there is no value in inheriting from a prototype. After that, it depends on how many instances you allocate of each type. But in many modular designs, there is only one instance of a given type.

And in fact, it has been suggested by many people that implementation inheritance is evil. That is, if there are some common operations for a type, then maybe it's clearer if they are not put into a base/super class, but are instead just exposed as ordinary functions in some module, to which you pass the object(s) you want them to operate on.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • 4
    Good explanation. Then would you agree that, since you consider prototypes to be an optimization, then they can always be used to improve your code? I'm wondering if there are cases where using prototypes does not make sense, or actually incurs a performance penalty. – opl Jan 19 '11 at 15:30
  • In your follow up, you mention that "it depends on how many instances you allocate of each type". But the example you refer to is not using prototypes. Where is the notion of allocating an instance (would you still be using "new" here)? Also: say the quack method had a parameter - would each invocation of duck.quack(param) cause a new object to be created in memory (maybe its irrelevant if it has a parameter or not)? – opl Jan 19 '11 at 16:37
  • 3
    **1.** I meant that if there were a large number of instances of one type of duck, then it would make sense to modify the example so that the `quack` function is in a prototype, to which the many duck instances are linked. **2.** The object literal syntax `{ ... }` creates an instance (there is no need to use `new` with it). **3.** Calling any function JS causes at least one object to be created in memory - it's called the `arguments` object and stores the arguments passed in the call: https://developer.mozilla.org/en/JavaScript/Reference/functions_and_function_scope/arguments – Daniel Earwicker Jan 19 '11 at 17:02
  • Thanks I accepted your answer. But I still have slight confusion with your point (1): I am not grasping what you mean by "large number of instances of one type of duck". Like you said in (3) each time you call a JS function, one object gets created in memory - so even if you just have one type of duck, wouldn't you be allocating memory each time you call a function of duck (in which case it would always make sense to use a prototype)? – opl Jan 19 '11 at 17:50
  • Firstly, using a prototype won't stop the `arguments` object being created. It is created every time any function is called, regardless of where the function is stored (in a local variable, an object property, etc.) Maybe the source of the confusion is something you've previously heard about objects being created only under some circumstances? In any case, it has no bearing on whether you use prototypes or not. – Daniel Earwicker Jan 19 '11 at 21:07
  • As for the problem with understanding my point about how the right approach depends on the number of instances, I'm not sure which part needs explaining. Do you understand the first code snippet that allocates a jQuery-like object using the object literal syntax? – Daniel Earwicker Jan 19 '11 at 21:14
  • Yes, I understood that example. I also understand what you meant about prototypes being similar to implementation inheritance and can be discouraged from and idealistic point of view. My confusion is why (or if), other then that reason, prototypes might be discouraged. From my understanding (which may be wrong), if one were to use your duck object (e.g. invoke quake()) more than a trivial amount of times then a prototype would be much more efficient. – opl Jan 19 '11 at 21:40
  • I guess the confusion is that with your jQuery-like object example it returns an object (so each time this its used, a new instance is created). But your duck example does not return anything, so using it does not seem to create a large number of instances. So i was confused as to how (given your example) you could create a large number of instances of duck as you mentioned. – opl Jan 19 '11 at 21:55
  • The code in the second example may execute a million times! :) Aside from that I think you understand it perfectly. – Daniel Earwicker Jan 20 '11 at 14:15
  • 11
    +1 The comparison with jQuery was the first clear & concise explanation of when & why to use prototypes that I've read. Thank you very much. – GFoley83 May 08 '13 at 21:54
  • It is much less possibilities to make things go wrong with prototypical inheritance. Because a) prototypal methods are unprevilages - so they can not change private state of the object and somehow make it inconsistent b) you can not override method in js like you can do this in JAVA - base methods call other base methods - logic is encapsulated i think this type of inheritance does not break encapsulation. Override is evil it is even "sealed" keyword in C#. c) Looks like prototypical inheritance has same features as composition ob objects that is pictured in Design Patterns of Gang of four. – Oleksandr Papchenko Aug 12 '15 at 19:46
  • About optimisation: methods exist only on one place in memory if you are using prototype. For me it looks like method area in Java. – Oleksandr Papchenko Aug 12 '15 at 19:49
49

You should use prototypes if you wish to declare a "non-static" method of the object.

var myObject = function () {

};

myObject.prototype.getA = function (){
  alert("A");
};

myObject.getB = function (){
  alert("B");
};

myObject.getB();  // This works fine

myObject.getA();  // Error!

var myPrototypeCopy = new myObject();
myPrototypeCopy.getA();  // This works, too.
KeatsKelleher
  • 10,015
  • 4
  • 45
  • 52
  • 1
    @keatsKelleher but we can create a non-static method for the object by just defining the method inside the constructor function using `this` example `this.getA = function(){alert("A")}` right ? – Amr Labib Jan 29 '18 at 20:20
18

One reason to use the built-in prototype object is if you'll be duplicating an object multiple times that will share common functionality. By attaching methods to the prototype, you can save on duplicating methods being created per each new instance. But when you attach a method to the prototype, all instances will have access to those methods.

Say you have a base Car() class/object.

function Car() {
    // do some car stuff
}

then you create multiple Car() instances.

var volvo = new Car(),
    saab = new Car();

Now, you know each car will need to drive, turn on, etc. Instead of attaching a method directly to the Car() class (which takes up memory per each instance created), you can attach the methods to the prototype instead (creating the methods only once), therefore giving access to those methods to both the new volvo and saab.

// just mapping for less typing
Car.fn = Car.prototype;

Car.fn.drive = function () {
    console.log("they see me rollin'");
};
Car.fn.honk = function () {
    console.log("HONK!!!");
}

volvo.honk();
// => HONK!!!
saab.drive();
// => they see me rollin'
hellatan
  • 3,517
  • 2
  • 29
  • 37
  • 2
    actually this is incorrect. volvo.honk() will not work becuase you completely replaced the prototype object, not extended it. If you were to do something like this it would work like you expect: Car.prototype.honk = function() { console.log('HONK');} volvo.honk(); //'HONK' – 29er Sep 16 '12 at 22:00
  • 1
    @29er - in the way i have written this example, you are correct. The order does matter. If i were to keep this example as is, the `Car.prototype = { ... }` would have to come before calling a `new Car()` as illustrated in this jsfiddle: http://jsfiddle.net/mxacA/ . As for your argument, this would be the correct way to do it: http://jsfiddle.net/Embnp/ . Funny thing is, i don't remember answering this question =) – hellatan Sep 17 '12 at 21:00
  • @hellatan you can fix that by setting constructor: Car to since you overwrote the prototype property with an object literal. – Josh Bedo Jan 08 '14 at 05:16
  • @josh thanks for pointing that out. I've updated my answer so that i don't overwrite the prototype with an object literal, as it should have been from the beginning. – hellatan Apr 26 '14 at 18:06
12

Put functions on a prototype object when you're going to create lots of copies of a particular kind of object and they all need to share common behaviors. By doing so, you'll save some memory by having just one copy of each function, but that's only the simplest benefit.

Changing methods on prototype objects, or adding methods, instantly changes the nature of all the instances of the corresponding type(s).

Now exactly why you'd do all these things is mostly a function of your own application design, and the kinds of things you need to do in client-side code. (A whole different story would be code inside a server; much easier to imagine doing more large-scale "OO" code there.)

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • so when I instantiate a new object with prototype methods (via new keyword), then that object doesn't get a new copy of each function (just kind of a pointer)? If this is the case, why would you not want to use a prototype? – opl Jan 19 '11 at 15:23
  • @opi yes, you're right - there's no copy made. Instead, the symbols (property names) on the prototype object are just sort of naturally "there" as virtual parts of each instance object. The only reason people would not want to bother with that would be cases where objects are short-lived and distinct, or where there's not much "behavior" to share. – Pointy Jan 19 '11 at 15:56
3

If i explain in class based term then Person is class, walk() is Prototype method. So walk() will have its existence only after you instantiate new object with this.

So if you want to create the copies of object like Person u can create many users Prototype is good solution as it saves memory by sharing/inheriting same copy of function for each of the object in memory.

Whereas static is not that great help in such scenario.

function Person(){
this.name = "anonymous";
}

// its instance method and can access objects data data 
Person.prototype.walk = function(){
alert("person has started walking.");
}
// its like static method
Person.ProcessPerson = function(Person p){
alert("Persons name is = " + p.name);
}

var userOne = new Person();
var userTwo = new Person();

//Call instance methods
userOne.walk();

//Call static methods
Person.ProcessPerson(userTwo);

So with this its more like instance method. The object's approach is like Static methods.

https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript

Anil Namde
  • 6,452
  • 11
  • 63
  • 100
3

Just including a video link here for reference, when not to use prototypes: https://youtu.be/JCXZhe6KsxQ?t=2m30s

Here is Ben Lesh's talk from NGConf, why rxjs removed patching prototype (chainable functions) in favor of pipeable functions.

Homan
  • 25,618
  • 22
  • 70
  • 107
  • This is pretty cool. Thanks for sharing. So, the RxJS 6+ will be focusing on the Functional Programming instead of OOP. – thewebjackal Sep 17 '21 at 07:17