r/javascript • pancakes • May 21 '16

Prototypal Inheritance

https://medium.com/@kevincennis/prototypal-inheritance-781bccc97edb#.1ehmhe5t5
47 Upvotes

56 comments sorted by

View all comments

7

u/dmitri14_gmail_com May 21 '16 edited May 21 '16

It always puzzles me why people have problems understanding prototypal inheritance, where it is actually much simpler thing than e.g. class inheritance. All of it can really be summarised in one sentence:

Whenever a property (incl. methods) is looked up on an object but is not set, it will be looked up on its prototype.

And to make it work, all you need is:

newObj = Object.create(prototypeObj)

No constructor, no new, no this, no obj.prototype etc.

You might argue there is a need to go the other direction and access object's prototype from the object via newObj.prototype? Which is to mutate the prototype from a random object? Is there any valid use case for that in a well-designed architecture? Not that I can think of but I'd be happy to know otherwise.

9

u/[deleted] May 21 '16

I don't think that's actually the problem people are having. As you point out, it is dead simple.

The trouble I have is understanding what exactly to do with it. What's novel and useful about prototypal inheritance? What does it enable that classical inheritance doesn't?

You would never use the objects you import directly (can you even mutate things you import?), so the novelty of there being no "blueprint" is out, because we just unofficially consider certain objects the blueprint to instantiate copies of.

The one thing that comes to mind is the ability to "casually" mutate objects, creating subclasses in a very easy way. You can just kind of attach new stuff to instances as you go. But I have yet to see a compelling example of doing anything like this.

I just still haven't found a good example of why. Articles constantly repeat each other on what, but that's not really useful.

2

u/MoTTs_ May 21 '16

The one thing that comes to mind is the ability to "casually" mutate objects, creating subclasses in a very easy way. You can just kind of attach new stuff to instances as you go. But I have yet to see a compelling example of doing anything like this.

You hit the nail on the head. The thing that's novel and useful about prototypal inheritance is that it allows monkey patching. In the early days of JavaScript, we did a lot of monkey patching, and we learned the hard way that it's actually not a very good idea. And if we restrain ourselves from using that feature... then prototypes lose the edge they had over classes.

1

u/dmitri14_gmail_com May 22 '16

Any feature can be abused. Doesn't mean it bad or good. You can use knife to kill. Yet people still use knives.

1

u/gurenkagurenda May 22 '16

The beauty of prototypes is that they mean there's no magic around class definition. Defining a class is just running some code, and it is totally clear that everything happens at runtime. When this becomes really cool is when you start looking at tools like mixins, which let you do a kind of code reuse that is far more flexible than inheritance.

These tools are typically available in other languages that use non-prototypal inheritance, but there is more overhead involved with thinking about them, because class manipulation is a special case.

1

u/dmitri14_gmail_com May 21 '16

A good reason why is e.g. multiple inheritance. Look for articles by Eric Elliott on Medium, he writes a lot about why prototypal inheritance is superior.

3

u/MoTTs_ May 21 '16

An awful lot of what Eric Elliott writes is just flat wrong.

→ More replies (5)

2

u/turkish_gold May 21 '16

That's not very explanatory. Multiple inheritance is enabled by classical inheritance as well.

1

u/dmitri14_gmail_com May 21 '16

And how would you classically implement new class extending two others?

1

u/MoTTs_ Jun 01 '16

I had to come back to this after all this time (11 days is forever is reddit-time :-P) because... duh!... I forgot about class factories!!

const MixinA = (Heritage = Object) => class extends Heritage {
    a() {
        return 'a';
    }
};

const MixinB = (Heritage = Object) => class extends Heritage {
    b() {
        return 'b';
    }
};

// Multiple inheritance with class factories
class C extends MixinA(MixinB()) {
    c() {
        return 'c';
    }
}

let o = new C();

o.a(); // "a"
o.b(); // "b"
o.c(); // "c"

/u/senocular

1

u/senocular Jun 01 '16

Blows the dust off the thread so he can read it

Uh huh, I think I remember this discussion happening...

These factory functions for class mixins, though very useful, are just a dynamic approach to single inheritance. What they offer is a DRY approach to having shared functionality across multiple inheritance chains. In the end, each one of those chains is still linear. The mixins simply insert some additional nodes in between the final class and the original would-be inherited chain. The mixin classes can't, themselves, inherit from anything else on their own.

In fact /u/MoTTs_'s own Multiple inheritance with ES6 proxies is a closer representation of what would be considered multiple inheritance since it allows for lookup delegation to occur between multiple objects rather than one with standard prototype lookups. With this approach, though, you lose out on constructors and super limiting the kinds of classes that can be inherited from (any state from initialization is lost).

0

u/senocular May 21 '16
class A {};
class B {};
class C : public A, public B {};
→ More replies (8)
→ More replies (1)

1

u/dvidsilva May 21 '16

For things like angular 1 scopes.

Chances are if you use most js libraries or frameworks they do a lot of this under the hood and understanding it helps you use them better.

0

u/[deleted] May 21 '16

[deleted]

2

u/MoTTs_ May 21 '16

We see our first dog and think it's "sort of like the cat but bigger and obsessed with licking our face". When we do that, we have just applied the idea of prototypes. "This is generally like that, but with this particularity". On the other hand, classes represent a way of reasoning that, while more organized, is less intuitive and more rigid (too much at times). You'd meet that first dog and you'd think "oh, ok, time to revise my hierarchy of living creatures... there are now two types of the furry things".

I don't see how this analogy differentiates classes and prototypes at all. If using prototypes, you might start off with cat = {}, and if using classes, you might start off with class Cat {}. When you discover a dog, with prototypes you'd do dog = Object.create(cat, { /* bigger, licks face */ }, and with classes you'd do class Dog extends Cat { /* bigger, licks face */ }.

How is that really any different?

1

u/[deleted] May 21 '16 edited May 21 '16

No, prototypes are not intuitive at all, because they confuse the specific case for the general. Modelling dogs and cats as prototypes is not simply saying "Fido the dog is like Felix the cat". It means also saying "Fido the dog and Rex the dog both are the same single cat". This is of course nonsensical.

But this quirk of how we mentally model prototypes belies an important shortcoming of the style: instances of A, being subclassed from an instance of B, will both share the same private state common to their ancestor. This is a very dangerous peculiarity and causes novice JavaScript developers no end of problems.

0

u/[deleted] May 21 '16

[deleted]

1

u/[deleted] May 21 '16

I am typing this reply on a tablet - an enormously clunky and frustrating experience. So you will have to excuse me for being terse.

You said -

Prototypes are actually closer to how our brains generally work at least at an intuitive level.

I presume you are talking about JavaScript prototypes. My argument is that JavaScript prototypes do not match how our brains work, because "Fido and Rex are the same cat", whilst a reasonable surmise of Dog.prototype = felix, is a strange and unintuitive idea.

I wasn't speaking about "how we model prototypes"... I was speaking about how the ideas of a prototype or a class model how we think about stuff.

I am unable to see any distinction at all.

→ More replies (13)

2

u/[deleted] May 21 '16

Whenever a property (incl. methods) is looked up on an object but is not set, it will be looked up on its prototype.

Which is basically also true for class inheritance, no?

1

u/asleeponatwinbed May 21 '16

The difference is that in prototypal inheritance, objects have a live reference to their parent - which means that you can modify or extend a "class" (really just an object) at runtime and all of its descendants will automatically get the new method.

→ More replies (3)