JavaScript When to Use Prototypes

Javascript when to use prototypes

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.

What’s the purpose of prototype?

Using the prototype makes faster object creation since properties/methods on the prototype don't have to be re-created each time a new object is created.

When you do this:

function animal() {
this.name = 'rover'
this.set_name = function (name) {
this.name = name
}
}

The set_name method is created every time you create an animal. But when you do this

animal.prototype.set_name = function (name) {
this.name = name
}

The method does not have to be re-created each time; it exists in one place in the prototype. So when you call someAnimal.set_name("Ubu"); the this context will be set to someAnimal and (the one and only) set_name method will be called.


There is one advantage to using the first syntax though: methods created in this manner will have access to private data:

function animal() {
var privateData = 'foo'

this.name = 'rover'
this.set_name = function (name) {
this.name = name
alert(privateData) //will alert 'foo'
}
}

Douglas Crockford calls methods created like this "privileged" for that reason: they have access to both public and private data.

JavaScript: When to use a Class vs. a prototype?

So, what is the difference between using a class vs. a prototype like below and when do you use either approach?

To answer your question simply, there is no real difference.

Straight from the MDN web docs definition:

JavaScript classes, introduced in ECMAScript 2015, are primarily syntactical sugar over JavaScript's existing prototype-based inheritance.

Should I use prototype or not?

This is exactly the situation for using prototype. I see two main benefits for doing so:

  1. Functions are not created multiple times. If you define the functions inside the constructor, a new anonymous function is being created for each function you define, every time the constructor is called. Prototypes are static objects, and each instance of Vector3D will simply reference the prototype functions.
  2. The prototype is a single object that can be easily manipulated. This affords great flexibility; unfortunately I'm only able to provide a few examples of what this can offer:
    1. If you wanted to create a child class, for example Vector3DSpecial, you can simply clone Vector3D.prototype and assign this to Vector3DSpecial.prototype. While you can also do this using constructors by Vector3DSpecial.prototype = new Vector3D();, constructors may contain side-effects which will get executed in that simple prototype assignment, and therefore should be avoided. With prototypes, you may even choose only particular functions in the prototype to be copied over to the new class.
    2. Adding methods to Vector3D is simply a matter of adding properties to the prototype, and allows your code to be more easily split / organised into multiple files, or to allow for adding methods in other parts of the code dynamically. Sure, you can do a combination of adding methods in the constructor and via the prototype, but that is inconsistent and is likely to lead to more complexity further down the track.

When would I not use prototype? For singleton objects, for example a controller that interacts with a page and may delegate work off to other objects. A global "notification" object is one such example. Here, extending is unlikely, and the object is only created once, making the prototype an additional (conceptual) complexity.

When should I use prototype in JavaScript

Simplest example:

function Foo() {
this.bar = function() {
return 42;
}
}

Now you can create as many instances of Foo as you want and call bar():

var a = new Foo();
var b = new Foo();

Even though both object have bar() method which is exactly the same in both cases, these are distinct methods. In fact, each new Foo object will have a new copy of this method.

On the other hand:

function Foo() {}
Foo.prototype.bar = function() {
return 42;
}

has the same end result but the function is stored only once in object prototype rather than in an object itself. This may be a deal breaker if you create tons of Foo instances and want to save some memory.

What is the use of prototype in Node js?

In short, prototype is used to build the interface (and implementation) of your custom-build objects in JavaScript.

As said Blender in the comment, it's how you do real OOP in JavaScript.

Do we still need prototype in ES6 to have one copy of a method shared across all class instances?

ES6 classes are syntactic sugar for established inheritance pattern that have been used in ES5.

ES6 classes use prototype methods by design. A method is shared among instances, so it's reasonable to define it once on prototype object and prototypically inherit it in instances. Prototypes are consistently optimized in modern engines and show performance benefits in some cases, so they can be preferred where appropriate.

My question was more about using a more modern way to achieve what I wanted, i.e. not having copies of methods.

Memory footprint can be reduced by reusing a function. This isn't a 'modern' way, just a way to address the issue:

function function f(){}
function FC() {
this.v = 42;
this.f = f;
};

A 'modern' way is to use prototype members.

FC function is not a direct counterpart to X class because the former assigns f on instance, not constructor prototype. A direct counterpart (with the exception of f descriptor, which is also non-enumerable in ES6 class) would be:

function FC() {
this.v = 42;
};
FC.prototype.f = function(){};

class X{
constructor(){
this.v = 'bar';
}
f(){}
}
// typeof X.prototype.f === 'function'

The reason why this style wasn't used consistently in ES5 is because this.f takes less characters to type and may be more readable than FC.prototype.f, while a developer may be unaware of the benefits and quirks of prototype.

The promotion of prototype members (methods and getters/setters) is one of few problems that class syntactic sugar addresses.



Related Topics



Leave a reply



Submit