By Pierreten


2010-05-10 07:22:04 8 Comments

So I finally stopped dragging my feet all these years and decided to learn JavaScript "properly". One of the most head-scratching elements of the languages design is it's implementation of inheritance. Having experience in Ruby, I was really happy to see closures and dynamic typing; but for the life of me can't figure out what benefits are to be had from object instances using other instances for inheritance.

5 comments

@Aadit M Shah 2013-06-01 11:44:31

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.

@redline 2013-06-01 23:20:09

While I understand where you are coming from, and I agree that prototypal inheritance is very useful, I think by making the assumption "prototypal inheritance is better than classical inheritance" you are already destined to fail. To give you some perspective, my library jTypes is a classical inheritance library for JavaScript. So being a guy that took the time to make that, I will still sit here and say prototypal inheritance is awesome and very useful. But it is simply just one tool amongst many that a programmer has. There are still many disadvantages to prototypal inheritance as well.

@Aadit M Shah 2013-06-02 03:00:46

@redline I never made any assumption like "prototypal inheritance is better than classical inheritance". Like I said in my blog post on Why Prototypal Inheritance Matters, "The truth is that classical inheritance is not bad. Python has classical inheritance and it’s a great programming language." I made a classical inheritance library augment too. I know what you mean. However I want more people to learn about true prototypal inheritance. That's it.

@Aadit M Shah 2013-06-02 03:07:11

@redline The point is that JavaScript is a prototypal language. Not a classical language. Most programmers start with trying to emulate classical inheritance in JavaScript, and it's really easy to do so. However they soon realize that true prototypal inheritance is better (at least in JavaScript). JavaScript is the most popular prototypal language. Unfortunately it uses the constructor pattern instead of the prototypal pattern, making understanding prototypal inheritance difficult.

@redline 2013-06-02 06:19:13

I completely agree with what you said. I feel that too many programmers reject JavaScript for its lack of classical inheritance, or denounce it as a simple and dumb language. It is actually an extremely powerful concept, I agree, and many programmers should accept it and learn it. With that being said, I also feel there are a significant number of developers in JavaScript that oppose any form of classical inheritance all together being in JavaScript, when they really have no basis for their arguments at all. Both are equally powerful in their own right, and equally useful as well.

@Aadit M Shah 2013-06-02 12:03:33

@redline, I hate to be the one to break your bubble but trying to emulate classical inheritance in JavaScript is a very bad idea. ECMAScript Harmony will have classes but it'll only be syntactic sugar for the constructor pattern. On the other hand I saw your jTypes library and I think it's totally going in the wrong direction. Classical inheritance is fine for Java or C++, but trying to emulate access modifiers, virtual classes, etc. in JavaScript is a very flaky solution. JavaScript is not meant for classical OOP. =)

@redline 2013-06-03 05:23:14

Well that is your opinion, but I will continue to disagree, and I think the growing popularity of things such as CoffeeScript and TypeScript show that there is a large community of developers who would like to utilize this functionality when appropriate. As you said, ES6 adds syntactic sugar, but still does not offer the extensiveness of jTypes. On a side note, I am not the one responsible for your downvote. While I disagree with you, I do not feel it constitutes you having a bad answer. You were very thorough.

@Aadit M Shah 2013-06-03 06:09:37

@redline, CoffeeScript is popular. TypeScript, not so much. Best of luck for your endeavors. Personally I feel adding access modifiers, etc. as jTypes does is overkill. You really don't need it in JavaScript. Your library targets a very small group of programmers who come from a classical inheritance background. Hence I doubt it will gain much momentum. On the other hand I browsed through your code and it was very impressive. You're a talented programmer. I hate to see such talent go to waste. I would suggest you think of another project which has more scope. Write a blog perhaps - that helps.

@Pavel Horal 2013-06-07 17:45:01

You are using word clone a lot, which is simply wrong. Object.create is creating new object with the specified prototype. Your choosing of words give impression that the prototype gets cloned.

@Aadit M Shah 2013-06-08 02:43:04

@PavelHoral I used the word "clone" for the lack of a better word. However I did distinguish the difference between delegation (linking to a prototype) and concatenation (copying the properties of a prototype). I used the word "clone" exclusively for delegation and the word "copy" exclusively for concatenation. Nevertheless I'll edit my answer to explain the meaning of clone in this context. Would you mind removing your downvote? I really don't think my answer deserves it just because of the choice of one word. If you can think of a better word then I'll happily replace "clone" with that word.

@Pavel Horal 2013-06-08 06:45:22

@AaditMShah IMO choosing "clone" is very bad idea. Better might be "attaching" / "linking". I still think that your answer is a little bit overkill in the question scope. Nevertheless downvote removed.

@Pierreten 2013-06-29 17:10:55

Had to give you the checkmark on this one. Excellently argued.

@samarjit samanta 2013-12-24 02:25:02

@AaditMShah Although I like your 4th point. Think in terms of maintaining a few thousand lines of multiple javascript files. We need to look up variables in very file. A little mistake in variable name creates havoc. Compilers don't point that mistake out. Good IDEs could not be written so far with auto suggestions or autocorrections. Variable names cannot be refactored. And IDEs cannot also suggest interfaces of 3rd party modules. Javascript programmers have to look up other's code to make sense. Java has jar files which can be introspected. Static typed language has all these advantages.

@samarjit samanta 2013-12-24 02:44:49

Javascript is a good language for its simplicity. But recent efforts to write large modular javascript applications has shown its shortcomings. Thus google(dart) and microsoft(TypeScript) both tried to come up with a typesystem for javascript which might help a better module system in javascript also enable them to optimize with JIT. Mozilla attempts to restrict javascript asmjs.org/spec/latest/#linking for optimization. If you see there will be some kind of inheritance in javascript in future: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/….

@Doug 2014-01-08 08:20:25

While I appreciate the rigor and enthusiasm of your answer, I don't think one must be a n00b to appreciate the beauty of languages designed for static analysis. Perhaps a way to approach this is to take your analogy of the paperwork (metadata) associated with ones dog. Let's say that paperwork provided a list of medical treatments administered to trhe dog. Sure, that information is not necessary for your dog to be a dog. But it gives the vet something to work with when treating your dog the next time.

@Doug 2014-01-08 08:31:23

--bottom line is that I trust myself to do the work of a compiler about as much as I trust myself to do math by hand. It works really well for simple problems. Like JavaScript. =)

@Aadit M Shah 2014-01-10 12:18:33

@DougR I think you got it all wrong. Static analysis is indeed a powerful and indispensable tool. However the static type systems of most classical object-oriented languages like Java and C++ are poor and obtrusive. Type systems work best when the compiler infers the type of your declarations. If you have to explicitly tell the compiler the type of every variable you declare then that's just added redundancy. Java does that. Haskell is smart. It infers types. The pedigree in the above context is a type not a data structure. It should be inferred. You shouldn't have to specify it explicitly. =)

@Aadit M Shah 2014-01-10 12:28:03

@DougR The bottom line is that static type systems are good. However Java and C++ have irreparably bad static type systems. In contrast Python has a great dynamic type system. It's unobtrusive and still catches most type errors. On the other hand JavaScript doesn't have a type system at all. Nevertheless in my humble opinion it's still better than Java because 1) the lack of a type system doesn't come in your way 2) you can build a static analysis tool for JavaScript (there are lots). The best type system however is that of Haskell. It is static, strong and smart enough to infer your types. =)

@Andy E 2014-03-20 08:43:21

clone is definitely a poor choice of words. A clone is defined as an exact copy of something else, and that isn't what's happening with constructors or Object.create. There's no copying going on. The way the answer is worded now, you run the risk of creating more confusion.

@Aadit M Shah 2014-03-20 12:15:01

@Andy What word would you suggest I use in lieu of "clone"?

@Andy E 2014-03-20 13:00:13

@AaditMShah: "linked" felt a little more appropriate from your answer, but perhaps an alternative description that shows an example of how JavaScript's prototypes actually work, maybe making use of __proto__ or [[Prototype]] to show that the new object doesn't actually contain those properties, but rather the objects linked by the prototype chain are searched when referencing or enumerating properties.

@Aadit M Shah 2014-03-20 15:23:41

@AndyE I fail to see how "linked" can be used as a drop in replacement for "clone". The former is a verb. The latter is, in addition, a noun. The noun "link" doesn't seem approbate in the context of the answer. I would be happy to replace "clone" with a more appropriate word. Nevertheless in want of a better word I'll leave my answer unchanged. Beside I abundantly elucidated the connotation of the terms I used in my answer, and seeing as to how many people upvoted it I opine that it actually clears confusion rather than contributing to it.

@Andy E 2014-03-20 16:50:57

@Aadit: there's really no need to be so defensive. Your answer is very detailed and deserving of votes. I wasn't suggesting that "linked" should be a drop-in replacement for "clone", but that it more appropriately describes the connection between an object and the prototype it inherits from, whether you assert your own definition of the term "clone" or not. Change it or don't change it, it's entirely your choice.

@Todd Lehman 2014-05-16 05:40:09

"protoclone", perhaps?

@Zemljoradnik 2014-12-07 09:17:32

While I find your answer (and your blog post on the same subject I read couple of months ago) informative, I fail to see how you "prove" that prototypal inheritance is better than classical inheritance, or why is Object.create(foo) approach better than new Foo(). And, IMO, there's no reason proving either is better. They are just different.

@Aadit M Shah 2014-12-07 10:00:38

@Zemljoradnik I agree. Both are equally bad.

@jrsala 2015-01-14 23:57:30

Lots of valid points but quite a few debatable ones. Also, answering "JavaScript supports private variables via closures" to the question of member access restrictions in JS is a sin that too many commit. The notion of access restrictions is completely absent at runtime and is meaningless to the program translation phase of the compilation. Its purpose is to help the programmer, and nothing else. The actual reason JS doesn't need private/protected/public is because no language needs them.

@A Fader Darkly 2015-02-20 11:32:42

Great answer, thank you. However, you say: "One of the most important advantages of prototypal inheritance is that you can add new properties to prototypes after they are created ... This is not possible in classical inheritance because once a class is created you can't modify it at runtime." This could just be another artefact of how badly Java sucks. Ruby uses classes and allows them to be modified at runtime, for example.

@leaf 2015-03-01 07:43:10

I agree with @redline, the assumption "prototypal better than classical" sounds wrong, imo it's a matter of perspective. For example, in m'y job I faced many situations where JavaScript permissiveness was a pain for team work. I mean, if I could work in a team made of gurus, I would assent to the proposal :-)

@trusktr 2015-09-20 18:22:44

You said "In contrast classical inheritance is much more complicated" and then listed a bunch of features in Java. Those are just patterns, and they can all be implemented in JavaScript. They just happen to already be implemented in Java out-of-the-box. IMO, Classical OOP is a pattern that can be implemented with JavaScript Prototypal object extension, or with concatenation, and exposed through an API.

@Michael Lafayette 2015-10-17 13:33:26

@AndyE - I think that the use of the word "clone" around "Object.create" was very intuitive because it made me thing that Object.create would copy the origional prototype rather than allowing the newly created object to modify it.

@Michael Lafayette 2015-10-17 13:39:34

As for the four points, I think that static analysis, auto-complete, pop-up documentation, and what not make my life so much easier that I cannot afford to give them up. In the past, I have implemented statically typed prototypal inheritance called Prototypal C in which all Objects are hashmaps that store both the data and also a 64 bit type value for each of the instance variables contained within. All functions and objects are hashmaps, just like in Javascript, but there is 64 bits of overhead for types and type checking. See: github.com/JohnReedLOL/Prototypal_C

@Michael Lafayette 2015-10-17 13:42:23

As for "private variables via closures", I find that to be unsightly. You are creating a function with a member variable that returns an object which contains a member variable that in turn is a function which refers to the private variable in the outside scope. Two objects instead of one. I find that hideous and convoluted.

@Michael Lafayette 2015-10-17 13:49:17

"this can be easily remedied by storing an array of prototypes on every object". Now you are extending object in a non-standard way to make up for functionality missing in the language. And you are creating a convention of always using your new copyOf method instead of instanceOf and isPrototypeOf, a convention which you must teach to every new programmer on your team, and which they must pass down. This is ugly - javascript simply isn't built for codebases meant to by used and updated by generations of people for decades to come.

@Michael Lafayette 2015-10-17 13:53:41

As for redundancy, I think you over-estimate that point. For example, say I am skimming through your code and I see something like: " var obj = FactoryOfFactories.makeObjectWith("fire", "ice", "metal") ". In this case, I have absolutely no idea what type is going to come out of this factory of factories, only that it is built with fire, ice, and metal. I have no idea what variables it has. I have no idea what methods it has. I have no way to use an IDE to pull up its methods. And unless you have really good documentation, I am pretty much screwed.

@Michael Lafayette 2015-10-17 14:02:06

Finally, "Prototypal Inheritance is Dynamic". This has nothing to do with the prototypical inheritance itself, but rather with the mechanism used to represent objects. In javascript, objects are represented as HashMaps, which are dynamic. You can add extra variables and functions to a HashMap (just like in my language Prototypal C). But it is also possible (in theory) to do Prototypal inheritance with static, fixed sized objects just as long as they have a pointer called "prototype". You could also have a language with fixed relationships in which objects are re-sizable. Prototypal != dynamic.

@Aadit M Shah 2015-10-20 05:07:56

@MichaelLafayette Wow. That's a lot to digest. Forgive me if I don't address all the points you make. Anyway, I just wanted to inform you that I no longer endorse my answer because I now believe that functional programming is better than most OOP languages. I'm not saying that OOP is bad. However, having used OOP languages for over 8 years I have realized that most of them are not in the same league as languages like Haskell and OCaml. In fact, the only OOP language I like anymore is Fortress, but I'm still stuck with JavaScript.

@Aadit M Shah 2015-10-20 05:15:20

@MichaelLafayette About Prototypal C, I don't understand the point that you're trying to make. Are you trying to say that it's possible to implement prototypal inheritance in C? Well, technically you can also implement prototypal inheritance in a Turing machine. That doesn't mean that you should. Speaking of which, what does static analysis, auto-complete, pop-up documentation, etc. have to do with anything?

@Aadit M Shah 2015-10-20 05:17:31

@MichaelLafayette When you're talking about my example of "private variables as closures" (i.e. the one which you find unsightly, hideous and convoluted) which example are you talking about? AFAIK, I didn't give any example of private variables as closures in my answer.

@Aadit M Shah 2015-10-20 05:23:16

@MichaelLafayette I agree. The copyOf solution is a very inelegant hack. That's the reason we use traits, mixins and type classes instead. Speaking of type classes, I believe that they are a much more elegant abstraction than "classes" in OOP languages.

@Aadit M Shah 2015-10-20 05:28:18

@MichaelLafayette The FactoryOfFactories example that you provided is very contrived. In fact, I would never write code like that so your entire argument is moot. I believe in self-explanatory code and opaque code like that is neither something that I would write nor teach anyone else to write.

@Aadit M Shah 2015-10-20 05:35:28

@MichaelLafayette When I said that "prototypal inheritance is dynamic" I meant that you can modify a prototype object and that change will be reflected in the instances of that prototype. In that sense, prototypal inheritance is indeed dynamic. Yes, this dynamic property belongs to the data representation of objects. Indeed, you can have prototypal OOP languages in which this dynamic property doesn't exist. However, I was trying to point out that you can't do this with classes. You can't modify a class after it's created and have the change reflected on its instances. That's all I was saying.

@Michael Lafayette 2015-10-20 05:36:52

@AaditMShah - One by one. I could implement a statically and strongly typed prototypal, functional, oop language that compiles into C and has nearly the same performance as C after full compilation and it would be nice to write in. Those three programming paradigms are not mutually exclusive. If you could allow objects in Scala to change their supertype at runtime and could make instances by copying other instances, it would be a strongly prototypal, functional, OOP language. All that prototypal means is objects have prototypes.

@Michael Lafayette 2015-10-20 05:39:30

@AaditMShah - "Speaking of which, what does static analysis, auto-complete, pop-up documentation, etc. have to do with anything?" These are advantages of a language whose hierarchies/relationships can be statically analyzed at runtime. Given that a prototypal language could allow you to set the parent one way in a branch and another way in another branch, good auto-complete is impossible and that is a disadvantage of prototypal vs OOP inheritance. That being said, I never type more than 3 consecutive characters without auto-complete, so this is a huge deal.

@Michael Lafayette 2015-10-20 05:40:40

@AaditMShah - the fact that Javascript requires that you double the numbers you instantiate just to make variables private is ugly. You may not notice it because you're used to it, but from someone looking at Javascript for the first time, it just looks messy and unnecessary.

@Michael Lafayette 2015-10-20 05:43:55

@AaditMShah - The factory of factories thing that takes in a String is a design pattern. I have seen and used real code that does stuff like "CodecFactory.makeCode(String s)" where you don't know the concrete type of what comes out and you have to figure out what that type will be and downcast to it. When the code explicitly states what type comes out, it can really makes factory methods clearer.

@Michael Lafayette 2015-10-20 05:49:35

@AaditMShah - "You can't modify a class after it's created and have the change reflected on its instances. That's all I was saying." Maybe I'm saying this because I am not a Javascript programmer, but I really don't think that making changes to the top of the proto chain in the middle of a program and having it propagate down to all the inheritors is a good idea. I mean I can understand removing an arm from a Person object or giving a Human object the fly function, or even re-classifying Crocodile from sub-typing Lizard to sub-typing Bird, but making all Crocodiles have a missing leg seems bad

@Dredok 2015-12-28 20:52:59

You mentioned several times C++ does not infer types, thats false since c++11 and c++14. You can use auto and bind it to an expresion, this will infer the type of the var. Also, for declaring data members you can use decltype and it will infer the type of the data member from given expression.

@Nawaz 2016-01-10 09:13:31

@MichaelLafayette: I dont understand how you could have prototypal inheritance in a static language. How would the compiler check for a member X, and its semantic, when it is going to be added at runtime? I assume prototypal inheritance allows you to add member at runtime, if you however differ at this assumption, then that is a different matter altogether.

@Michael Lafayette 2016-01-10 13:41:34

1. Make a class that contains a hashmap of sharedpointers that point to structs that contain a 64 bit type value and a void * of data. Every time you add something to the hashmap that has a type, the type is extracted as a 64 bit type id and the data is converted to void * and the name of the variable is converted into a string or symbol for retrieval. Every time you want to get the variable out of the container, the 64 bit type id is recombined with the void *. If the type matches what you are trying to assign it to, it compiles. If the type does not match, you get an error.

@Michael Lafayette 2016-01-10 13:43:21

@Nawaz - By removing the type on assignment and adding the type back in on retrieval, the variables that are obtained from the container have a type - a strong type.

@Michael Lafayette 2016-01-10 13:46:05

@Nawaz - Note that members such as variables and functions are all added at runtime and that these hash-objects can have a parent that they defer their retrival to if they cannot find a variable or function name inside of their body.

@Michael Lafayette 2016-01-10 13:47:48

@Nawaz - So you don't exactly have a static inheritance hierarchy. You have a dynamic inheritance hierarchy, so it is impossible to know if something is accessible from a given object or not because you don't know which object will be its parent at compile time. The inheritance hierarchy is dynamic but every object and function has a static type id.

@Michael Lafayette 2016-01-10 13:49:29

@Nawaz - Just imagine Python, but you can change what object a given object inherits from at runtime and you can add members and functions at runtime and the compiler doesn't know if those members or functions are in the object and the compiler doesn't know which object is the parent, but it does know that each object has a type.

@Michael Lafayette 2016-01-10 13:54:42

@Nawaz - But for example if you put an int in an object, you get an int out. If you put a string in, you get a string out. int and string have no parent, and they're not allowed to have a parent, and you're not allowed to add members or functions to them at runtime, so if you try to call a function that is not in such an object, the compiler can yell at you.

@Nawaz 2016-01-10 14:12:22

I agree that it can be done.. and that is what is sorta done by Javascript (or Python using prototype.py). But then, once you have such a language, the language is no more a static language, which in turn means the things "static analysis, auto-complete, pop-up documentation" you talked about in one of your comment are not available to you, at least in this context.

@giorgim 2016-11-12 17:30:01

@AaditMShah Hey, are you really describing inheritance? With inheritance I would expect setup like having a structure say Shape and then you would show how to derive Circle from Shape say. But you are exclusively working with Circles all the time.

@Aadit M Shah 2016-11-12 19:26:52

@GiorgiMoniava Yes, it is really inheritance. We're taking something abstract (i.e. a description of a circle) and making something concrete (i.e. a particular circle). That's inheritance in a nutshell. I think you're getting confused because prototypes conflate instantiation with inheritance. In classical languages you'd have a Circle class and you'd be able to make instances of Circle using new. You'd consider this different from inheritance. In prototypal languages they're the same thing. Read the following answer I wrote for more information stackoverflow.com/a/19640910/783743

@bugwheels94 2017-03-16 13:33:42

I still don't get how you inherit in multiple inheritance mode using _.extend and only worry about instanceof and isPrototypeOf while it is also not meeting the point number 4 'Prototypal inheritance in dynamic'. And the remedy suggested in point no. 2 for multiple inheritance is still not clear to me but it looks pretty nasty in ambiguous form. I have read your lots of answers and all of these just try to add unnecessary complications to ECMA Script and all answers are based on so strict policy instead of flexible policy which is bad for programmers freedom. Why?

@Jacob Zimmerman 2018-09-27 21:01:40

I don't see anything that a dynamic classical inheritance language can't do (other than the supposed "simplicity"), like Python. In point 2, you talk about multiple inheritance via copying, but that nullifies your dynamic updates of prototypes. 90% of the redundancy you mention in point 3 is a syntactical thing. I will admit that you can't create an object and then decide to layer on more things to inherit from later. 1 point for prototypal! Your last point about dynamicism is also possible in Python's classical inheritance. That's a property of languages, not inheritance systems.

@s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼ 2019-12-04 06:46:34

Thanks for your good explanation! I don't know about classical inheritance, did you answer this before? Or could you recommend some good article about classical inheritance? Thanks again!

@Aadit M Shah 2019-12-04 08:38:56

@s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼ It's been a while since I wrote this answer. My views have changed since I wrote it. Now, I believe that inheritance, both classical and prototypal, are bad and should be avoided at all costs. There's never a good reason to use inheritance. If you're writing object-oriented code then you should always prefer composition over inheritance. Even better, don't write object-oriented code. Embrace functional programming.

@Noel Abrahams 2012-09-20 12:59:43

There is really not a lot to choose between the two methods. The basic idea to grasp is that when the JavaScript engine is given a property of an object to read, it first checks the instance and if that property is missing, it checks up the prototype chain. Here is an example that shows the difference between prototypal and classical:

Prototypal

var single = { status: "Single" },
    princeWilliam = Object.create(single),
    cliffRichard = Object.create(single);

console.log(Object.keys(princeWilliam).length); // 0
console.log(Object.keys(cliffRichard).length); // 0

// Marriage event occurs
princeWilliam.status = "Married";

console.log(Object.keys(princeWilliam).length); // 1 (New instance property)
console.log(Object.keys(cliffRichard).length); // 0 (Still refers to prototype)

Classical with instance methods (Inefficient because each instance stores it's own property)

function Single() {
    this.status = "Single";
}

var princeWilliam = new Single(),
    cliffRichard = new Single();

console.log(Object.keys(princeWilliam).length); // 1
console.log(Object.keys(cliffRichard).length); // 1

Efficient classical

function Single() {
}

Single.prototype.status = "Single";

var princeWilliam = new Single(),
    cliffRichard = new Single();

princeWilliam.status = "Married";

console.log(Object.keys(princeWilliam).length); // 1
console.log(Object.keys(cliffRichard).length); // 0
console.log(cliffRichard.status); // "Single"

As you can see, since it is possible to manipulate the prototype of "classes" declared in the classical style, there is really no benefit to using prototypal inheritance. It is a subset of the classical method.

@A Fader Darkly 2015-02-20 12:29:33

Looking at other answers and resources about this topic, your answer would appear to be stating: "Prototypal inheritance is a subset of the syntactic sugar added to JavaScript to allow the appearance of classical inheritance". The OP appears to be asking about the benefits of prototypal inheritance in JS over classical inheritance in other languages rather than comparing instantiation techniques within JavaScript.

@CMS 2010-05-10 08:16:11

IMO the major benefit of prototypal inheritance is its simplicity.

The prototypal nature of the language can confuse people who are classically trained, but it turns out that actually this is a really simple and powerful concept, differential inheritance.

You don't need to make classification, your code is smaller, less redundant, objects inherit from other, more general objects.

If you think prototypically you will soon notice that you don't need classes...

Prototypal inheritance will be much more popular in the near future, the ECMAScript 5th Edition specification introduced the Object.create method, which allows you to produce a new object instance that inherits from another one in a really simple way:

var obj = Object.create(baseInstance);

This new version of the standard is being implemented by all browser vendors, and I think we will start to see more pure prototypal inheritance...

@Noel Abrahams 2012-09-20 11:10:24

"your code is smaller, less redundant...", why? I've had a look at the wikipedia link for "differential inheritance" and there is nothing supporting these assertions. Why would classical inheritance result in larger, more redundant code?

@redline 2013-06-01 23:26:46

Exactly, I agree with Noel. Prototypal inheritance is simply one way to get a job done, but that doesn't make it the right way. Different tools will perform in different ways for different jobs. Prototypal inheritance has its place. It is an extremely powerful and simple concept. With that being said, the lack of support for true encapsulation and polymorphism puts JavaScript at a significant disadvantage. These methodologies have been around much longer than JavaScript has, and they are sound in their principals. So thinking prototypal is "better" is just the wrong mentality altogether.

@Pavel Horal 2013-06-07 17:31:06

You can simulate class-based inheritance using prototype-based inheritance, but not vice versa. That might be a good argument. Also I see encapsulation more as a convention than language feature (usually you can break encapsulation via reflection). Regarding polymorphism - all you gain is not having to write some simple "if" conditions when checking method arguments (and a little bit of speed if the target method is resolved during compilation). No real JavaScript disadvantages here.

@aoeu256 2019-08-16 02:24:50

Prototypes are awesome, IMO. I am thinking of creating a functional language like Haskell... but instead of creating abstractions I'll base everything on prototypes. Instead of generalizing sum, and factorial to "fold" you should the prototype of the sum function and replace the + with * and 0 with 1 to make product. I'll explain "Monads" as prototypes that replace "then" from promises/callbacks with flatMap being a synonym of "then". I think prototypes can help bring functional programming to the masses.

@JUST MY correct OPINION 2010-05-10 08:11:04

Allow me to actually answer the question inline.

Prototype inheritance has the following virtues:

  1. It is better suited to dynamic languages because the inheritance is as dynamic as the environment it is in. (The applicability to JavaScript should be obvious here.) This permits you to do things quickly on the fly like customizing classes without huge amounts of infrastructure code.
  2. It is easier to implement a prototyping object scheme than the classic class/object dichotomy schemes.
  3. It eliminates the need for the complex sharp edges around the object model like "metaclasses" (I never metaclass I liked... sorry!) or "eigenvalues" or the like.

It has the following disadvantages however:

  1. Type checking a prototype language isn't impossible, but it's very, very difficult. Most "type checking" of prototypical languages is pure run-time "duck typing"-style checks. This is not suitable to all environments.
  2. It is similarly difficult to do things like optimizing method dispatch by static (or, often, even dynamic!) analysis. It can (I stress: can) be very inefficient very easily.
  3. Similarly object creation can be (and usually is) much slower in a prototyping language than it can be in a more conventional class/object dichotomy scheme.

I think you can read between the lines above and come up with the corresponding advantages and disadvantages of traditional class/object schemes. There are, of course, more in each area so I'll leave the rest up to other people answering.

@siliconrockstar 2019-01-15 02:03:21

Hey look, a concise answer that doesn't fanboy. Really wish this were the top answer for the question.

@aoeu256 2019-07-09 00:09:59

We have Dynamic Just-in-time compilers today that can compile the code while the code is running creating different pieces of code for each section. JavaScript is actually faster than Ruby or Python that use classical classes because of this even if you use prototypes because lots of work has been done on optimizing it.

@ratty 2010-05-10 07:27:49

Web Development: Prototypal Inheritance vs. Classical Inheritance

http://chamnapchhorn.blogspot.com/2009/05/prototypal-inheritance-vs-classical.html

Classical Vs prototypal inheritance - Stack Overflow

Classical Vs prototypal inheritance

@James Westgate 2010-05-10 07:53:41

I think its better to summarise the contents of links rather than pasting the link (something I myself used to do), unless its another SO link. This is because links/sites go down and you loose the response to the question, and it potentially affects the search results.

@viebel 2012-03-11 10:34:09

The 1st link doesn't answer the question of why prototypal inheritance? It simply describes it.

Related Questions

Sponsored Content

22 Answered Questions

[SOLVED] When to use self over $this?

  • 2008-09-30 06:23:06
  • Casey Watson
  • 681132 View
  • 1956 Score
  • 22 Answer
  • Tags:   php class oop scope

26 Answered Questions

[SOLVED] Why not inherit from List<T>?

33 Answered Questions

[SOLVED] For-each over an array in JavaScript?

3 Answered Questions

[SOLVED] prototype based vs. class based inheritance

32 Answered Questions

[SOLVED] Prefer composition over inheritance?

6 Answered Questions

[SOLVED] Python class inherits object

25 Answered Questions

[SOLVED] How does JavaScript .prototype work?

6 Answered Questions

15 Answered Questions

Sponsored Content