Prototype Based VS. Class Based Inheritance

prototype based vs. class based inheritance

There are about a hundred terminology issues here, mostly built around someone (not you) trying to make their idea sound like The Best.

All object oriented languages need to be able to deal with several concepts:

  1. encapsulation of data along with associated operations on the data, variously known as data members and member functions, or as data and methods, among other things.
  2. inheritance, the ability to say that these objects are just like that other set of objects EXCEPT for these changes
  3. polymorphism ("many shapes") in which an object decides for itself what methods are to be run, so that you can depend on the language to route your requests correctly.

Now, as far as comparison:

First thing is the whole "class" vs "prototype" question. The idea originally began in Simula, where with a class-based method each class represented a set of objects that shared the same state space (read "possible values") and the same operations, thereby forming an equivalence class. If you look back at Smalltalk, since you can open a class and add methods, this is effectively the same as what you can do in Javascript.

Later OO languages wanted to be able to use static type checking, so we got the notion of a fixed class set at compile time. In the open-class version, you had more flexibility; in the newer version, you had the ability to check some kinds of correctness at the compiler that would otherwise have required testing.

In a "class-based" language, that copying happens at compile time. In a prototype language, the operations are stored in the prototype data structure, which is copied and modified at run time. Abstractly, though, a class is still the equivalence class of all objects that share the same state space and methods. When you add a method to the prototype, you're effectively making an element of a new equivalence class.

Now, why do that? primarily because it makes for a simple, logical, elegant mechanism at run time. now, to create a new object, or to create a new class, you simply have to perform a deep copy, copying all the data and the prototype data structure. You get inheritance and polymorphism more or less for free then: method lookup always consists of asking a dictionary for a method implementation by name.

The reason that ended up in Javascript/ECMA script is basically that when we were getting started with this 10 years ago, we were dealing with much less powerful computers and much less sophisticated browsers. Choosing the prototype-based method meant the interpreter could be very simple while preserving the desirable properties of object orientation.

Understanding the difference between classical and prototypal inheritance

It seems to me that you actually already got the point.

Class based OO

As you already mentioned, in class-based object oriented languages (like Java) a class is a blueprint for every future object. For the instantiation of an object the blueprint (the class you created) gets copied for this object. This means that in case you change the class after you instantiate your object the previously created object won't have that changes.

When it comes to inheritance: Let's say you have a class Person. You want to create another class Student that inherits from Person. Student is basically a copy of the blueprint Person which can extend the functionality of the blueprint. So your example is very accurate!
This is very useful in complex but rather static OO-structures!

Prototype based

Prototype based languages like JavaScript don't follow this templating kind of approach. Your example hit it pretty good again. You basically just create an object and reference to another object (your prototype). All the functionality you put in your prototype will be shared by all the objects that reference to that prototype object. However, it is important to understand that you don't make copies of blueprints or templates. You are always working with objects.

So you could create a Person Object. That holdsmethods like "sayHello". If you create concrete person e.g. Joe you just create another object and link it to the Person object. So if you try Joe.sayHello() it will look through the Joe object properties and won't find the sayHello method so it will jump over to the concrete Person object. Just like you said with your example.

I don't really like the expression inheritance in prototype based languages because it does not really exist. You don't make a copy of a blueprint and extend its functionality. Inheritance basically works by chaining objects together. For instance the example above. You can have a Person object. Every regular person object (e.g. Joe) has the Person Object as a prototype. Now you create a Student object which has the Person as a prototype. Another object (e.g. StudentJoe) can have Student as a prototype. So it can go through the chain all the way up to person to access its methods.
To keep it short ;): prototype based languages only work with concrete objects which are linked together (prototypes) and not with blue prints and templates.
The advantage of this approach is being dynamic (that's why this approach is commonly used in the web). As you never work with templates but with concrete objects every change in the prototype-chain will always have effect on every object (in terms of accessable functionality) - no matter when it was created.

I hope this helped. There is a good book called You don't know JavaScript - this & object prototype which explains this topic based on JavaScript really well.

What does it mean that Javascript is a prototype based language?

Prototypal inheritance is a form of object-oriented code reuse. Javascript is one of the only [mainstream] object-oriented languages to use prototypal inheritance. Almost all other object-oriented languages are classical.

In classical inheritance, the programmer writes a class, which defines an object. Multiple objects can be instantiated from the same class, so you have code in one place which describes several objects in your program. Classes can then be organized into a hierarchy, furthering code reuse. More general code is stored in a higher-level class, from which lower level classes inherit. This means that an object is sharing code with other objects of the same class, as well as with its parent classes.

In the prototypal inheritance form, objects inherit directly from other objects. All of the business about classes goes away. If you want an object, you just write an object. But code reuse is still a valuable thing, so objects are allowed to be linked together in a hierarchy. In javascript, every object has a secret link to the object which created it, forming a chain. When an object is asked for a property that it does not have, its parent object will be asked... continually up the chain until the property is found or until the root object is reached.

Each function in JavaScript (which are objects themselves) actually has a member called "prototype", which is responsible for providing values when an object is asked for them. Having this member allows the constructor mechanism (by which objects are constructed from functions) to work. Adding a property to the prototype of a function object will make it available to the constructed object, as well as to all of the objects which inherit from it.

Advantages

There may not be a hard and fast rule as to why prototypal inheritance is an advantageous form of code-reuse. Code reuse itself is advantageous, and prototypal inheritance is a sensible way of going about it. You might argue that prototypal inheritance is a fairly simple model of code reuse, and that code can be heavily reused in direct ways. But classical languages are certainly able to accomplish this as well.

Sidenote: @Andrew Hedges makes a good point, that there are actually many prototypal languages. It's worth noting that these others exist, but also worth noting that none of them are anything close to mainstream. NewtonScript seemed to have some traction for a while, but died with its platform. It's also possible to extend some modern languages in ways which add prototypal capabilities.

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.

Is inheritance in Lua prototype-based or class-based?

Lua is prototype-based.

Technically, there is no class or inheritance or base class, super class in Lua. Lua has some tools like metatables to simulate classical OOP. The main reason that the PiL book uses these terms is to make it easier for the reader to understand.

If you like the language-lawyer way of describing the syntax, read the reference manual, you won't find these terms there.

Class compared to Prototype inheritance

They are functionally exactly the same. Here are some interesting properties about prototypes and classes to point out though:

class Dog1 {    constructor(name){      this.name = name    }
speak (){ // adds speak method to the prototype of Dog1 console.log('Woof') }}
Dog1.prototype.speak = () => { // overwrites speak method to the prototype of Dog1 console.log('Different Woof');}
console.log(typeof Dog1); // Class keyword is just syntactic sugar for constructor function
const newDog = new Dog1('barkie');
newDog.speak();
/////////////////////////////
function Dog2(name){ this.name = name;}
Dog2.prototype.speak = () => { console.log('Woof');}
const aDog = new Dog2('fluffy');
aDog.__proto__.speak(); // the __proto__ property references the prototypeaDog.speak(); // the __proto__ property is not necessary, it will automatically climb the protochain if the property is not found on the object itself.

The difference between a class-based language (like Java or Python) and a prototype-based language (like Javascript)?

It seems like you're familiar with the actual languages, so you know what the difference is, right? I guess you're asking about the differences at a deeper, maybe more "philosophical", level.

Class-based languages tend to work from the top down, general to particular. The classic example would be where you define a 'Vehicle' class, and then subclasses like 'Car', 'Train'.

A prototype-based language would instead tend to start with the particular, in fact start with an instance of the particular and modify that.

I like this: http://steve-yegge.blogspot.ie/2008/10/universal-design-pattern.html

In the end it's not a question of if you can do inheritance in JS or whether there is something that you can do in one language but not the other. It's a deep difference in their ways of approaching problem solving. For a particular problem a good idiomatic solution that made best use of the language's features would probably be quite different in a prototype-based language from one in a class-based language.

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.

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



Related Topics



Leave a reply



Submit