Benefits of Prototypal Inheritance Over Classical

Benefits of prototypal inheritance over classical?

I know that this answer is 3 years late but I really think the current answers do not provide enough information about how prototypal inheritance is better than classical inheritance.

First let's see the most common arguments JavaScript programmers state in defence of prototypal inheritance (I'm taking these arguments from the current pool of answers):

  1. It's simple.
  2. It's powerful.
  3. It leads to smaller, less redundant code.
  4. It's dynamic and hence it's better for dynamic languages.

Now these arguments are all valid, but nobody has bothered explaining why. It's like telling a child that studying Maths is important. Sure it is, but the child certainly doesn't care; and you can't make a child like Maths by saying that it's important.

I think the problem with prototypal inheritance is that it's explained from the perspective of JavaScript. I love JavaScript, but prototypal inheritance in JavaScript is wrong. Unlike classical inheritance there are two patterns of prototypal inheritance:

  1. The prototypal pattern of prototypal inheritance.
  2. The constructor pattern of prototypal inheritance.

Unfortunately JavaScript uses the constructor pattern of prototypal inheritance. This is because when JavaScript was created, Brendan Eich (the creator of JS) wanted it to look like Java (which has classical inheritance):

And we were pushing it as a little brother to Java, as a complementary language like Visual Basic was to C++ in Microsoft’s language families at the time.

This is bad because when people use constructors in JavaScript they think of constructors inheriting from other constructors. This is wrong. In prototypal inheritance objects inherit from other objects. Constructors never come into the picture. This is what confuses most people.

People from languages like Java, which has classical inheritance, get even more confused because although constructors look like classes they don't behave like classes. As Douglas Crockford stated:

This indirection was intended to make the language seem more familiar to classically trained programmers, but failed to do that, as we can see from the very low opinion Java programmers have of JavaScript. JavaScript’s constructor pattern did not appeal to the classical crowd. It also obscured JavaScript’s true prototypal nature. As a result, there are very few programmers who know how to use the language effectively.

There you have it. Straight from the horse's mouth.

True Prototypal Inheritance

Prototypal inheritance is all about objects. Objects inherit properties from other objects. That's all there is to it. There are two ways of creating objects using prototypal inheritance:

  1. Create a brand new object.
  2. Clone an existing object and extend it.

Note: JavaScript offers two ways to clone an object - delegation and concatenation. Henceforth I'll use the word "clone" to exclusively refer to inheritance via delegation, and the word "copy" to exclusively refer to inheritance via concatenation.

Enough talk. Let's see some examples. Say I have a circle of radius 5:

var circle = {
radius: 5
};

We can calculate the area and the circumference of the circle from its radius:

circle.area = function () {
var radius = this.radius;
return Math.PI * radius * radius;
};

circle.circumference = function () {
return 2 * Math.PI * this.radius;
};

Now I want to create another circle of radius 10. One way to do this would be:

var circle2 = {
radius: 10,
area: circle.area,
circumference: circle.circumference
};

However JavaScript provides a better way - delegation. The Object.create function is used to do this:

var circle2 = Object.create(circle);
circle2.radius = 10;

That's all. You just did prototypal inheritance in JavaScript. Wasn't that simple? You take an object, clone it, change whatever you need to, and hey presto - you got yourself a brand new object.

Now you might ask, "How is this simple? Every time I want to create a new circle I need to clone circle and manually assign it a radius". Well the solution is to use a function to do the heavy lifting for you:

function createCircle(radius) {
var newCircle = Object.create(circle);
newCircle.radius = radius;
return newCircle;
}

var circle2 = createCircle(10);

In fact you can combine all of this into a single object literal as follows:

var circle = {
radius: 5,
create: function (radius) {
var circle = Object.create(this);
circle.radius = radius;
return circle;
},
area: function () {
var radius = this.radius;
return Math.PI * radius * radius;
},
circumference: function () {
return 2 * Math.PI * this.radius;
}
};

var circle2 = circle.create(10);

Prototypal Inheritance in JavaScript

If you notice in the above program the create function creates a clone of circle, assigns a new radius to it and then returns it. This is exactly what a constructor does in JavaScript:

function Circle(radius) {
this.radius = radius;
}

Circle.prototype.area = function () {
var radius = this.radius;
return Math.PI * radius * radius;
};

Circle.prototype.circumference = function () {
return 2 * Math.PI * this.radius;
};

var circle = new Circle(5);
var circle2 = new Circle(10);

The constructor pattern in JavaScript is the prototypal pattern inverted. Instead of creating an object you create a constructor. The new keyword binds the this pointer inside the constructor to a clone of the prototype of the constructor.

Sounds confusing? It's because the constructor pattern in JavaScript unnecessarily complicates things. This is what most programmers find difficult to understand.

Instead of thinking of objects inheriting from other objects they think of constructors inheriting from other constructors and then become utterly confused.

There's a whole bunch of other reasons why the constructor pattern in JavaScript should be avoided. You can read about them in my blog post here: Constructors vs Prototypes


So what are the benefits of prototypal inheritance over classical inheritance? Let's go through the most common arguments again, and explain why.

1. Prototypal Inheritance is Simple

CMS states in his answer:

In my opinion the major benefit of prototypal inheritance is its simplicity.

Let's consider what we just did. We created an object circle which had a radius of 5. Then we cloned it and gave the clone a radius of 10.

Hence we only need two things to make prototypal inheritance work:

  1. A way to create a new object (e.g. object literals).
  2. A way to extend an existing object (e.g. Object.create).

In contrast classical inheritance is much more complicated. In classical inheritance you have:

  1. Classes.
  2. Object.
  3. Interfaces.
  4. Abstract Classes.
  5. Final Classes.
  6. Virtual Base Classes.
  7. Constructors.
  8. Destructors.

You get the idea. The point is that prototypal inheritance is easier to understand, easier to implement, and easier to reason about.

As Steve Yegge puts it in his classical blog post "Portrait of a N00b":

Metadata is any kind of description or model of something else. The comments in your code are just a a natural-language description of the computation. What makes metadata meta-data is that it's not strictly necessary. If I have a dog with some pedigree paperwork, and I lose the paperwork, I still have a perfectly valid dog.

In the same sense classes are just meta-data. Classes aren't strictly required for inheritance. However some people (usually n00bs) find classes more comfortable to work with. It gives them a false sense of security.

Well, we also know that static types are just metadata. They're a specialized kind of comment targeted at two kinds of readers: programmers and compilers. Static types tell a story about the computation, presumably to help both reader groups understand the intent of the program. But the static types can be thrown away at runtime, because in the end they're just stylized comments. They're like pedigree paperwork: it might make a certain insecure personality type happier about their dog, but the dog certainly doesn't care.

As I stated earlier, classes give people a false sense of security. For example you get too many NullPointerExceptions in Java even when your code is perfectly legible. I find classical inheritance usually gets in the way of programming, but maybe that's just Java. Python has an amazing classical inheritance system.

2. Prototypal Inheritance is Powerful

Most programmers who come from a classical background argue that classical inheritance is more powerful than prototypal inheritance because it has:

  1. Private variables.
  2. Multiple inheritance.

This claim is false. We already know that JavaScript supports private variables via closures, but what about multiple inheritance? Objects in JavaScript only have one prototype.

The truth is that prototypal inheritance supports inheriting from multiple prototypes. Prototypal inheritance simply means one object inheriting from another object. There are actually two ways to implement prototypal inheritance:

  1. Delegation or Differential Inheritance
  2. Cloning or Concatenative Inheritance

Yes JavaScript only allows objects to delegate to one other object. However it allows you to copy the properties of an arbitrary number of objects. For example _.extend does just this.

Of course many programmers don't consider this to be true inheritance because instanceof and isPrototypeOf say otherwise. However this can be easily remedied by storing an array of prototypes on every object which inherits from a prototype via concatenation:

function copyOf(object, prototype) {
var prototypes = object.prototypes;
var prototypeOf = Object.isPrototypeOf;
return prototypes.indexOf(prototype) >= 0 ||
prototypes.some(prototypeOf, prototype);
}

Hence prototypal inheritance is just as powerful as classical inheritance. In fact it's much more powerful than classical inheritance because in prototypal inheritance you can hand pick which properties to copy and which properties to omit from different prototypes.

In classical inheritance it's impossible (or at least very difficult) to choose which properties you want to inherit. They use virtual base classes and interfaces to solve the diamond problem.

In JavaScript however you'll most likely never hear of the diamond problem because you can control exactly which properties you wish to inherit and from which prototypes.

3. Prototypal Inheritance is Less Redundant

This point is a little more difficult to explain because classical inheritance doesn't necessarily lead to more redundant code. In fact inheritance, whether classical or prototypal, is used to reduce the redundancy in code.

One argument could be that most programming languages with classical inheritance are statically typed and require the user to explicitly declare types (unlike Haskell which has implicit static typing). Hence this leads to more verbose code.

Java is notorious for this behavior. I distinctly remember Bob Nystrom mentioning the following anecdote in his blog post about Pratt Parsers:

You gotta love Java's "please sign it in quadruplicate" level of bureaucracy here.

Again, I think that's only because Java sucks so much.

One valid argument is that not all languages which have classical inheritance support multiple inheritance. Again Java comes to mind. Yes Java has interfaces, but that's not sufficient. Sometimes you really need multiple inheritance.

Since prototypal inheritance allows for multiple inheritance, code which requires multiple inheritance is less redundant if written using prototypal inheritance rather than in a language which has classical inheritance but no multiple inheritance.

4. Prototypal Inheritance is Dynamic

One of the most important advantages of prototypal inheritance is that you can add new properties to prototypes after they are created. This allows you to add new methods to a prototype which will be automatically made available to all the objects which delegate to that prototype.

This is not possible in classical inheritance because once a class is created you can't modify it at runtime. This is probably the single biggest advantage of prototypal inheritance over classical inheritance, and it should have been at the top. However I like saving the best for the end.

Conclusion

Prototypal inheritance matters. It's important to educate JavaScript programmers on why to abandon the constructor pattern of prototypal inheritance in favor of the prototypal pattern of prototypal inheritance.

We need to start teaching JavaScript correctly and that means showing new programmers how to write code using the prototypal pattern instead of the constructor pattern.

Not only will it be it easier to explain prototypal inheritance using the prototypal pattern, but it will also make better programmers.

If you liked this answer then you should also read my blog post on "Why Prototypal Inheritance Matters". Trust me, you will not be disappointed.

Classical Vs prototypal inheritance

Prototype-based inheritance is more flexible. Any existing object can become a class from which additional objects will be spawned. This is handy where your objects offer several sets of services and/or they undergo a lot of state transformation before your program arrives at the point where the inheritance is needed.

A broad-spectrum discussion of modeling approaches is available here: http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html

classical inheritance vs prototypal inheritance in javascript

Both the code samples you demonstrated in your question make use of prototypal inheritance. In fact any object-oriented code you write in JavaScript is a paradigm of prototypal inheritance. JavaScript simply doesn't have classical inheritance. This should clear things up a bit:

                                   Inheritance
|
+-----------------------------+
| |
v v
Prototypal Classical
|
+------------------------------+
| |
v v
Prototypal Pattern Constructor Pattern

As you can see prototypal inheritance and classical inheritance are two different paradigms of inheritance. Some languages like Self, Lua and JavaScript support prototypal inheritance. However most languages like C++, Java and C# support classical inheritance.



A Quick Overview of Object-Oriented Programming

Both prototypal inheritance and classical inheritance are object-oriented programming paradigms (i.e. they deal with objects). Objects are simply abstractions which encapsulate the properties of a real world entity (i.e. they represent real word things in the program). This is known as abstraction.

Abstraction: The representation of real world things in computer programs.

Theoretically an abstraction is defined as "a general concept formed by extracting common features from specific examples". However for the sake of this explanation we're going to use the aforementioned definition instead.

Now some objects have a lot of things in common. For example a mud bike and a Harley Davidson have a lot in common.

A mud bike:

A mud bike.

A Harley Davidson:

A Harley Davidson

A mud bike and a Harley Davidson are both bikes. Hence a bike is a generalization of both a mud bike and a Harley Davidson.

                   Bike
|
+---------------------------------+
| |
v v
Mud Bike Harley Davidson

In the above example the bike, the mud bike and the Harley Davidson are all abstractions. However the bike is a more general abstraction of the mud bike and the Harley Davidson (i.e. both the mud bike and the Harley Davidson are specific types of bikes).

Generalization: An abstraction of a more specific abstraction.

In object-oriented programming we create objects (which are abstractions of real world entities) and we use either classes or prototypes to create generalizations of these objects. Generalizations are created via inheritance. A bike is a generalization of a mud bike. Hence mud bikes inherit from bikes.



Classical Object-Oriented Programming

In classical object-oriented programming we have two types of abstractions: classes and objects. An object, as mentioned before, is an abstraction of a real world entity. A class on the other hand is an abstraction of an object or another class (i.e. it's a generalization). For example, consider:

+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity | Comments |
+----------------------+----------------+---------------------------------------+
| 0 | John Doe | Real World Entity. |
| 1 | johnDoe | Variable holding object. |
| 2 | Man | Class of object johnDoe. |
| 3 | Human | Superclass of class Man. |
+----------------------+----------------+---------------------------------------+

As you can see in classical object-oriented programming languages objects are only abstractions (i.e. all objects have an abstraction level of 1) and classes are only generalizations (i.e. all classes have an abstraction level greater than 1).

Objects in classical object-oriented programming languages can only be created by instantiating classes:

class Human {
// ...
}

class Man extends Human {
// ...
}

Man johnDoe = new Man();

In summation in classical object-oriented programming languages objects are abstractions of real world entities and classes are generalizations (i.e. abstractions of either objects or other classes).

Hence as the level of abstraction increases entities become more general and as the level of abstraction decreases entities become more specific. In this sense the level of abstraction is analogous to a scale ranging from more specific entities to more general entities.



Prototypal Object-Oriented Programming

Prototypal object-oriented programming languages are much simpler than classical object-oriented programming languages because in prototypal object-oriented programming we only have one type of abstraction (i.e. objects). For example, consider:

+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity | Comments |
+----------------------+----------------+---------------------------------------+
| 0 | John Doe | Real World Entity. |
| 1 | johnDoe | Variable holding object. |
| 2 | man | Prototype of object johnDoe. |
| 3 | human | Prototype of object man. |
+----------------------+----------------+---------------------------------------+

As you can see in prototypal object-oriented programming languages objects are abstractions of either real world entities (in which case they are simply called objects) or other objects (in which case they are called prototypes of those objects that they abstract). Hence a prototype is a generalization.

Objects in prototypal object-oriented programming languages may be created either ex-nihilo (i.e. out of nothing) or from another object (which becomes the prototype of the newly created object):

var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);

In my humble opinion prototypal object-oriented programming languages are more powerful than classical object-oriented programming languages because:

  1. There is only one type of abstraction.
  2. Generalizations are simply objects.

By now you must have realized the difference between classical inheritance and prototypal inheritance. Classical inheritance is limited to classes inheriting from other classes. However prototypal inheritance includes not only prototypes inheriting from other prototypes but also objects inheriting from prototypes.



Prototype-Class Isomorphism

You must have noticed that prototypes and classes are very similar. That's true. They are. In fact they are so similar that you can actually use prototypes to model classes:

function CLASS(base, body) {
if (arguments.length < 2) body = base, base = Object.prototype;
var prototype = Object.create(base, {new: {value: create}});
return body.call(prototype, base), prototype;

function create() {
var self = Object.create(prototype);
return prototype.hasOwnProperty("constructor") &&
prototype.constructor.apply(self, arguments), self;
}
}

Using the above CLASS function you can create prototypes that look like classes:

var Human = CLASS(function () {
var milliseconds = 1
, seconds = 1000 * milliseconds
, minutes = 60 * seconds
, hours = 60 * minutes
, days = 24 * hours
, years = 365.2425 * days;

this.constructor = function (name, sex, dob) {
this.name = name;
this.sex = sex;
this.dob = dob;
};

this.age = function () {
return Math.floor((new Date - this.dob) / years);
};
});

var Man = CLASS(Human, function (Human) {
this.constructor = function (name, dob) {
Human.constructor.call(this, name, "male", dob);
if (this.age() < 18) throw new Error(name + " is a boy, not a man!");
};
});

var johnDoe = Man.new("John Doe", new Date(1970, 0, 1));

The reverse is not true however (i.e. you can't use classes to model prototypes). This is because prototypes are objects but classes are not objects. They are an entirely different type of abstraction.



Conclusion

In summation we learned that an abstraction is a "a general concept formed by extracting common features from specific examples" and that generalization is "an abstraction of a more specific abstraction". We also learned about the differences between prototypal and classical inheritance and how both of them are two faces of the same coin.

On a parting note I would like to remark that there are two patterns of prototypal inheritance: the prototypal pattern and the constructor pattern. The prototypal pattern is the canonical pattern of prototypal inheritance whereas the constructor pattern is used to make prototypal inheritance look more like classical inheritance. Personally I prefer the prototypal pattern.

Understanding why true prototypal inheritance is better than classical/pseudo prototypal inheritance and why i shouldn't use new

Similar questions have been asked and answered many times before. See:

Constructor function vs Factory functions
Classical Vs prototypal inheritance

More learning:
https://medium.com/javascript-scene/3-different-kinds-of-prototypal-inheritance-es6-edition-32d777fa16c9#.s0r3i5w6t
http://vimeo.com/69255635

tl;dr

  • Constructors break the open / closed principle
  • Constructors conflate object creation with object initialization - sometimes hampering the reusability of the code
  • Constructors look a bit like classes, which is confusing. JavaScript doesn't need classes (I recommend avoiding the class keyword coming in ES6). JavaScript has something better than classes.
  • The combination of prototype delegation and dynamic object extension (concatenative inheritance) is much more powerful and flexible than classical inheritance.
  • The connections between the Constructor.prototype and instances are frail and untrustworthy in JavaScript. Using constructors can provide the illusion of a working instanceof, which could be confusing when it doesn't work across execution contexts, or doesn't work if the constructor prototype gets swapped out.
  • Swapping out the prototype is harder with constructors. You may want to do that to enable polymorphic object construction. With factories, hot swappable prototypes are easy, and can be done using .call() and .apply().

Edit - responding to the "answer" posted by the OP:

The best thing about Object.create is that it's a dedicated, low-level tool that lets you create a new object and assign any prototype you want to it without using a constructor function. There are lots of reasons to avoid constructors, covered in-depth here: Constructor function vs Factory functions


  1. The code you use to demonstrate "less code" doesn't really demonstrate the difference between classical and prototypal inheritance at all. A more typical example might look like:

Classical

var Animal = function Animal(name) {
this.name = name;
};

Animal.prototype.walk = function walk() {
console.log(this.name + ' goes for a walk.');
};

var Rabbit = function Rabbit(/* name */) {
// Because construction and instantiation are conflated, you must call super().
Animal.prototype.constructor.apply(this, arguments);
};

// Classical inheritance is really built on top of prototypal inheritance:
Rabbit.prototype = Object.create(Animal.prototype);

// Fix the .constructor property:
Rabbit.prototype.constructor = Rabbit;

Rabbit.prototype.jump = function jump() {
console.log(this.name + ' hops around a bit.');
};

var myRabbit = new Rabbit('Bunny George');

myRabbit.walk();
// Bunny George goes for a walk.

Prototypal

var animalMethods =  {
walk: function walk() {
console.log(this.name + ' goes for a walk.');
}
};

var animal = function animal(name) {
var instance = Object.create(animalMethods);
instance.name = name;
return instance;
};

var rabbitMethods = {
jump: function jump() {
console.log(this.name + ' hops around a bit.');
}
};

var rabbit = function rabbit(name) {
var proto = rabbitMethods;

// This is more commonly done like mixin({}, animalMethods, rabbitMethods);
// where mixin = $.extend, _.extend, mout.object.mixIn, etc... It just copies
// source properties to the destination object (first arg), where properties from
// the last argument override properties from previous source arguments.
proto.walk = animalMethods.walk;
var instance = Object.create(rabbitMethods);

// This could just as easily be a functional mixin,
// shared with both animal and rabbit.
instance.name = name;
return instance;
};

var rabbit2 = rabbit('Bunny Bob');

rabbit2.walk();
// Bunny Bob goes for a walk.

The amount of code required is pretty similar, but to me, it's a LOT more clear what the prototypal stuff is doing, and it's also a lot more flexible, and has none of the classical inheritance arthritic baggage of the first example.

What are the advantages that prototype based OO has over class based OO?

The advantage of prototypal inheritance is that it potentially allows fancy metaprogramming in a straightforward way because the prototype chain is easily manipulated. This is a rather academic advantage because metaprogramming is the wrong answer 99% of the time. As an example, you could have a Javascript Key-Value Observer style data manipulation layer with a special DSL that transparently switched between a local SQLite backing when offline and a REST based server store when online via prototype swapping. I'm not sure it's the best way to do this, but it's the best I can come up with this late. It's not the sort of thing you generally want to do in project code since this sort of indirection is hell to debug once you start getting it going on multiple layers, but it's not bad when you keep it in a library.

Another less helpful advantage is that it allows you to design your own class system. I say less helpful because more or less all javascript libraries have their own slightly incompatible approach to how 'classes' are put together.

There are a lot of people replying who are mixing the inheritance model with the languages implemented in that model. The fact that javascript is dynamic and weakly typed and thus hard to tool has nothing to do with it being a prototypal language.

Difference between classical inheritance and prototype inheritance

In JavaScript, class inheritance is implemented on top of prototypal inheritance, but that does not mean that it does the same thing:

In addition to inheriting properties, class inheritance does extra wiring to link the child [[Prototype]] to the parent [[Prototype]]. Usually, the super() constructor is also called. Those extra steps form parent/child hierarchies and create the tightest coupling available in OO design.

Hence, "Classes inherit from classes and create subclass relationships: hierarchical class taxonomies."

It's also useful to understand that there is more than one kind of prototypal OO. Importantly, there is concatenative inheritance, and prototype delegation.

Concatenative inheritance is important, because that's what allows for simple (and very common) object composition in JavaScript. Remember the Gang of Four said, "favor object composition over class inheritance."

This is generally accepted OO design wisdom, and because of concatenative inheritance, it's a breeze to do that in JavaScript.



Related Topics



Leave a reply



Submit