Fluent JavaScript – Three Different Kinds of Prototypal OO

JavaScript 53 53 Comments

Note: I’ve presented this as a talk a couple of times. You can see the latest version from O’Reilly’s Fluent Conference: JavaScript and Beyond, 2013. The talk is called “Classical Inheritance is Obsolete: How to Think in Prototypal OO”.

In order to claim fluency in JavaScript, it’s important to understand how JavaScript’s native inheritance capabilities work. This is an often neglected area of JavaScript writing and learning, but understanding it can be dramatically empowering.

JavaScript is one of the most expressive programming languages ever created. In particular, its combination of delegate prototypes, runtime object extension, and closures allow you to express three distinct types of prototypes in JavaScript. Let’s take a closer look at each of these.

Delegation / Differential Inheritance

A delegate prototype is an object that serves as a base for another object. When you inherit from a delegate prototype, the new object gets a reference to the prototype. When you try to access a property on the new object, it checks the object’s own properties first. If it doesn’t find it there, it checks the prototype, and so on up the chain until it gets back to Object.prototype.

Method delegation is a fantastic way to preserve memory resources, because you only need one copy of each method to be shared by all instances. It’s also a great way to add capabilities at runtime to all objects which share a particular prototype.

There are a couple of ways to set up that relationship in JavaScript. The one you’re likely to see in a lot of books goes something like this:

See JavaScript Constructor Functions vs Factory Functions and Stop Using Constructor Functions in JavaScript for my thoughts on why you should ignore this technique. I present it here only because it’s likely to be a familiar point of reference.

I prefer this:

The one major drawback to delegation is that it’s not very good at storing state. In particular, if you try to store state as objects or arrays, mutating any member of the object or array will mutate the member for every instance that shares the prototype. In order to preserve instance safety, you need to make a copy of the state for each object.

Cloning / Concatenative Inheritance / Mixins

Prototype cloning is the process of copying the properties from one object to another, without retaining a reference between the two objects. Cloning a great way to store default state for objects. This process is commonly achieved by methods like Underscore’s .extend(), or jQuery’s .extend():

It’s common to see this style used for mixins. For example, Backbone users can make any object an event emitter by extending from Backbone.Events:

Closure Prototypes / Functional Inheritance

I’m cheating on the name for this one. It’s not really functional, and it’s not an object prototype. It’s a function prototype. Think of it as an alternative to a constructor / init function. It can be copied (inherited) from one factory to another, and combined with other functions like it to completely replace the need for super() (which is a code smell, and should be avoided).

Closure prototypes are functions that can be run against a target object in order to extend it. The primary advantage of this style is that it allows for encapsulation. In other words, you can enforce private state. Douglas Crockford called this style “functional inheritance” in his book, “JavaScript: The Good Parts”. It looks something like this (Foo, like above, with private attributes):

This is all well and good, but there’s an awful lot of jumping through hoops if you want to combine the techniques — so I wrote a little library to jump through the hoops for you. It’s called Stampit.

Stampit

Create objects from reusable, composable behaviors.

Features

  • Create functions (called factories) which stamp out new objects. All of the new objects inherit all of the prescribed behavior.
  • Compose factories together to create new factories.
  • Inherit methods and default state.
  • Supports composable private state and privileged methods.
  • State is cloned for each instance, so it won’t be accidentally shared.
  • For the curious – it’s great for learning about prototypal OO. It mixes three major types of prototypes:
    1. differential inheritance, aka delegation (for methods),
    2. cloning, aka concatenation/exemplar prototypes (for state),
    3. functional / closure prototypes (for privacy / encapsulation)
  • What’s the Point?

    Prototypal OO is great, and JavaScript’s capabilities give us some really powerful tools to explore it, but it could be easier to use.

    Basic questions like “how do I inherit privileged methods and private data?” and “what are some good alternatives to inheritance hierarchies?” are stumpers for many JavaScript users.

    Let’s answer both of these questions at the same time. First, we’ll use a closure to create data privacy:

    It uses function scope to encapsulate private data. Note that the getter must be defined inside the function in order to access the closure variables.

    Let’s see if that worked:

    Yes. Got it. In both of these instances, we actually created a brand new object, and then immediately threw it away, because we didn’t assign it to anything. Don’t worry about that.

    Here’s another:

    Those a‘s are not a typo. The point is to demonstrate that a and b‘s private variables won’t clash.

    But here’s the real treat:

    WAT? Yeah. You just inherited privileged methods and private data from two sources at the same time.

    But that’s boring. Let’s see what else is on tap:


    1. IMO, mixins work best when added to prototypes.

      One advantage of constructors is that many engines optimize for them. Even though I really like the prototypal approach [1], I’ve resigned myself to fitting in and now only use constructors. They will become further entrenched in ECMAScript 6, via classes.

      [1] http://www.2ality.com/2011/06/prototypes-as-classes.html

      • Eric Elliott - February 14, 2013

        Axel – I agree that mixins work best when added to prototypes. You can extend a new prototype like this:

        `Object.create(_.extend({}, aProto, someMethods));

        “One advantage of constructors is that many engines optimize for them.”

        There really isn’t a big performance difference. In fact, there isn’t much different going on functionally, at all, except that the constructor has to jump through **extra hoops** to interpret new and set up this. If you follow best practices and do the instanceof check in your constructors, that’s another step (and potentially another method call), as well. In other words, while they can optimize away, they’re really just trying to figure out how to speed up what is inherently a slower process than just running a normal function. Throw the common block of this.foo = that; into the mix (vs something like return Object.create(someProto);), and you have a recipe for slow.

        See JavaScript Constructor Functions vs Factory Functions.

        As for that prototypes-as-classes link — what they’re saying is that the new class keyword in JavaScript is going to work a lot more like the methods I prescribed in this article. One major difference — I do not advocate building deep inheritance chains. Keep it as flat as you can.

        Thanks for your feedback!

        • Here is a jsPerf showing the performance difference:

          http://jsperf.com/object-create-vs-constructor-vs-object-literal/49

          Spoiler: constructors are 28 times faster than the fastest usage of Object.create.

          • Eric Elliott - February 17, 2013

            Looks pretty bad if all you look at is prototype assignment, right? But that finding gets flipped on its head if you look at instance property assignment (the bit that actually varies between objects). See this factory vs constructor perf.

            It’s pretty easy to tell the story you want to tell, just by being selective about what you test. And since Object.create() is a native method, when we see more use in the wild, it will get the same type of optimization attention that constructors got — and since the operation is inherently less complicated, it could potentially be optimized even better (it doesn’t have to mess with new or set up instanceof, or branch for different return behaviors in the constructor function).

            • There is no Object.create anywhere in this jsPerf you posted. Returning an object literal is of course faster than constructor usage, which is still significantly faster than Object.create.

              As you say, after more usage in the wild the optimization could improve and it could “potentially” be optimized even better but in today’s real-world JavaScript, constructors are much faster than Object.create.

            • Eric Elliott - February 25, 2013

              All fair points, but I’d be careful not to put too much weight on this aspect of it, because:

              1) That particular difference is very likely to get optimized away in the engines — possibly soon, and
              2) If you really want the best possible performance, you should be using factories that return object literals, instead of constructor functions or Object.create() — but this is unlikely to become a bottleneck in a typical JavaScript application. Far more likely culprits will be your choice of algorithms, data structures, I/O, DOM manipulations, or page reflows.

              It’s good to be aware of performance implications, but it’s a bit silly to point at perf results and conclude that the technique is never useful, simply because there’s another technique that happens to have a better performance profile. Sometimes a slight perf tradeoff is worth it, because of the gains in clarity or flexibility in your code.

        • There’s actually a **HUGE** performance difference between cloning objects using constructors, and cloning objects using Object.create. However, the performance of all other operations on the object after you’ve created it have pretty much the same performance. You can see a test case for this here: http://jsperf.com/object-creation-access/2 (That test case is almost two years old now, and I really should have separated the operations since it’s a pain to compare in this state. But you can use the Table view).

          Anyways, from a practical standpoint, Object.create is likely to perform okay in all situations even with the unfair overt-optimisation of creation through constructors. For example, my library for property-based testing in JavaScript (a “port” of ScalaCheck/QuickCheck) is prototypes all the way down, using Object.create exclusively, and even though every possible operation clones the previous object for immutability, it still has an acceptable performance (also note that this makes the delegation chain *huge*). The architecture is also much different than most other JS applications because you’re probably not creating a thousand of objects every millisecond ­— unless you’re writing a game engine.

          So, Object.create’s performance is not great, but it is acceptable today for most stuff. Ditching it for constructors just because they’re faster now isn’t really a good argument.

          • Eric Elliott - March 7, 2013

            If you’re making a game engine and generating rapid-fire bullets or something at the tens-of-thousands-per-second rate, constructors still don’t give you best case performance. Object literals do. Unless each object has a lot of big methods attached to it, in which case I’d say your object architecture needs re-thinking. =)

            So if you’re optimizing for speed, you still don’t want to use constructors.

    2. Am I missing something, or the only way to actually have a “inheritance” chain is by using a constructor or Object.create which is basically a empty constructor ??

    3. Max Shishkin - February 15, 2013

      Just few days ago I was looking for some cleaner and more flexible solution to plain classic-like inheritance via prototypes, and now I’ve found one! Really like it, will try to use in the upcoming project. Thanks!

    4. I really like this approach and Stampit seems very convenient and elegant.

      The one thing that’s always bugged me about javascript inheritance is that there’s no built-in “super”-like keyword, even though other purely prototypal languages like self and Io have this feature. It makes it really difficult to method overrides with more than 1 level of inheritance.

      • Eric Elliott - February 16, 2013

        I don’t suggest that you use more than one level of inheritance, and I find that, while I do use method overrides and prototypal inheritance together, the only thing I do that’s like super is the stacking of functional inheritance that happens when you compose multiple stamps which implement closures (the .enclose() feature).

        I find that trying to use something like super tends to get error prone and hard-to-debug quickly.

      • You can implement “super” in your js lib. See Backbone-Super or jquery.inherit

        • Eric Elliott - February 17, 2013

          I have no intention of implementing super. Martin Fowler thinks super is a code smell, and I agree. I have a strong distaste for the tight coupling it requires between the child and the parent. As soon as you give the child instance a handle to the thing its inheriting from, you’ve broken encapsulation, and reintroduced the same brittle architecture we were trying to get away from in the first place when we switched to prototypal OO.

          Instead of super, there are a million things you can do: facade, dependency injection, “abstract interface”, etc… The last one presents something interesting (and the one Martin Fowler recommends). You can implement base features and then provide hooks for the “concrete” instances. I put quotes around those of course because every object in JavaScript is a concrete instance, but in this case, the “abstract” version defines an exemplar interface that the concrete object can implement using hooks — the hooks are the bits you’re supposed to override, whereas the interface bits should not be overridden.

          For example, if you were building a media player, you could define .play(), .pause(), and .volume() methods on the interface. Lets say all of these operations require some business logic that must be implemented regardless of the tech — for instance, the .play() method needs to check to see if the user is authorized to play the media.

          These methods can be set up to call hooks: .techPlay(), .techPause(), and .techVolume(). Now you create an html5Player and a flashPlayer. They return objects that define the .tech* methods. Using stampit, you could inherit from both the base media player and a tech player to create your working player instances.

          I would argue sternly that defining yet another level of inheritance which needs to override those same methods again is probably a bad idea. With each level of method override you add, you increase the complexity and opportunity for trouble exponentially.

          Besides, it’s more likely that what you really need to do is emit an event from .play() that any object can listen for in order to implement additional behaviors or manage cross-cutting concerns.

    5. I am (was?) in the same boat as Max. Will definitely be trying this out. Thanks!

    6. Greg Baker - March 22, 2013

      Great find! …nice read.

      I recently did something similar in search of a better way – need was secondary at the time. My solution was far more convoluted, less flexible and incomplete (yet fully tested.) So this is timely for me. I will go back and see where I went astray; but I may simply scrap it for your small lib instead. Thanks for the article and coce.

      Greg

      • Eric Elliott - April 6, 2013

        This is my second go at a library like this. It’s easy to second guess other people’s design choices, but much harder to crank out a solid design yourself. My first attempt had some good things going for it, but ultimately fell short of the mark, and it was more complicated than it needed to be. I think this one hit the sweet spot. At least for me. Let me know what you think if you decide to use it. =)

    7. Thiago Figueiredo - May 15, 2013

      Great article.

      Also, this bit in one of your comments should be repeated over and over again:
      “It’s good to be aware of performance implications, but it’s a bit silly to point at perf results and conclude that the technique is never useful, simply because there’s another technique that happens to have a better performance profile. Sometimes a slight perf tradeoff is worth it, because of the gains in clarity or flexibility in your code.”

      To the new and the old guys. To ourselves too.

    8. Mike Lippert - May 19, 2013

      Eric,
      Stampit sounds interesting, and you make some reasonable arguments for writing JS code in this style (I’m new to JS and trying to figure out how best to structure my project and currently trying to get my head around Closure), but it needs real documentation rather than just some simple examples.

      The readme says “you can chain methods…” and provides an example that doesn’t tell me what methods is or why I would want to chain them.

      I don’t have any familiarity w/ _.extend(), $.extend(), and ES6 Object.mixIn(), so referencing them as the sole documentation for methods and state isn’t very helpful.

      I’m looking forward to understanding more though.

      • Eric Elliott - May 19, 2013

        Hi Mike,

        Very good point. I have updated the Stampit documentation. Does that help?

        • Mike Lippert - May 23, 2013

          Hi Eric,
          Sorry it me a while to check back.

          Yes that is much better, thank you. Having the API described like that lets me go back and look at the examples and really start understanding on how I could apply this to my design.

    9. Christian - May 22, 2013

      Hi Eric,

      in medias res: you seem to know your stuff ;-) And so does Andrea Giammarchi.
      I am really curious what you think about his way of achieving inheritance in Javascript:
      http://webreflection.blogspot.hu/2012/12/a-cross-platform-inherit-function.html

      Though his coding style is nowhere nearly as easy to decipher (at least for me), what caught my interest in his post was (among additional things) the part where he writes: 

      Yeah, one of the coolest things about being able to inherit from null is the fact not even Object.prototype is inherited so we can create real empty objects without worrying about surrounding environment, 3rd parts obtrusive libraries, and the boring and slow obj.hasOwnProperty(key) check.

      That’s correct, objects that inherit from null are not affected by the Object.prototype. Really, we cannot even print them without defining a toString method!

      From a purist’s point of view, I feel that having such a truly blank state for objects and constructs to be build upon sounds very appealing (and even secure, maybe?)

      Thanks in advance
      Christian

      • Eric Elliott - May 23, 2013

        That implementation is a bit convoluted and serves little purpose. The existing MDN Object.create polyfill works great if all you want is the first Object.create argument, and if you want to be able to loop over an object without worrying about hasOwnProperty, you could just use a utility like mout/object/forOwn — IMO, a much less brittle way of accomplishing the same goal. You’ll note that he’s jumping through a lot of hoops and assuming browser environments for something that should be generically useful in any JavaScript environment. What about Node?

        In future versions of JavaScript, this will all be a moot point, because we’ll have a native own property loop.

    10. Michael Bennett - July 2, 2013

      I haven’t played with the code yet, but this looks really solid. I’m especially attracted to composing factories from other factories. I wish I’d been able to read this three years ago before I learned a lot of this the hard way.

      Actually who am I kidding, I wouldn’t have understood, I went to school in the late nineties when everyone was shoving Java, and classical OOP down people’s throats. I had to learn JS the hard way, and I’m still learning.

      Thanks for this library, and the talks. I hope this helps more people embrace the power of the lambda.

    11. Benjamin Gruenbaum - July 16, 2013

      You know someone is not serious when they call classical inheritance ‘obsolete’. In the JavaScript world, classical inheritance and more accurately structural (rather than behavioral) subtyping is still a useful design pattern that has several use cases. 

      Pretending that it’s not is lying to yourself. The strength in JS is that it lets you do prototypical OOP _in addition_ to classical OOP.

      • Eric Elliott - July 31, 2013

        I’ve heard this a lot from people with classical OO backgrounds (much like mine), but I’ve never seen any actual evidence to back it up. Please spell out some of those use cases, and why they are better than composition or factory functions.

      • Eric Elliott - July 31, 2013

        BTW, I’m quite serious. I don’t believe that there are any use cases where classical inheritance is a better fit than composition, delegation, or functional code reuse (or a combination of the three).

    12. Matt Wistrand - July 26, 2013

      By setting methods directly in the function prototype…
      var model = function() {
          this.set = function() {};
      };

      do you not lose the memory benefits of a delegation prototype, since those methods need to be recreated as separate instances each time model.call() is executed? At which point should we care about this?

      • Eric Elliott - July 31, 2013

        Hi Matt,

        You do lose those memory benefits. If memory is a consideration, you should be setting them on the delegate prototype, rather than the instance. If you need the benefits of the closure, you’re explicitly deciding that you need memory allocation at the instance level (usually for encapsulation of instance state). That’s when you’d use the functional variety instead of the delegate prototype.

    13. This is a beautiful concept. One note though: do you really think the mixin and closure prototypes are different enough to merit their own categories? I could see the model being simplified to just two types of “inheritance”: delegate which involves properties of the prototype, and mixin which involves properties of the object itself. In terms of API, you could have two methods .delegate() and .mixin() that could each take as argument either an object (in which case it would do straight copying) or a function which would be used to create closures. This could simplify your implementation as well, as you could have a single private method behind the scenes handling both cases.

      • I went ahead and whipped up an implementation of my model here: https://github.com/tsherif/oFactory

        • Eric Elliott - August 13, 2013

          Tarek – Impressive work. I disagree with your assumption that it’s better with two methods, instead of three, but that doesn’t make your implementation any less interesting.

          If you’re interested in pursuing it as a viable alternative to Stampit, take this gist and drop it into the stampit test folder to get an idea of the level of feature parity you have achieved… I took the liberty of adding some aliases so the Stampit tests would work:

          https://gist.github.com/dilvie/6223652

    14. james - August 21, 2013

      Erik,
      I’ve  been reading, in the middle of, various books.. js ninja, functional js, allonge etc.. reading blogs etc.. and i am having difficulty wrapping my head around the level of eloquence and difficulty you bring. How does one reach this level of technique? Write and create js etc…? My work does not bring the need for this level BUT i want to reach it.

      Any advice? Tutorials? Books?

    15. Quick question, how different this code is than a regular closure at the end you are calling a(); and getting an object back. also isn’t a good idea to just use closures for Web applications ?! if we write a good quality code, we will never face memory leaks.

      var a = stampit().enclose(function () {
      var a = ‘a’;
      this.getA = function () {
      return a;
      };
      });

      thanks
      mahdi

      • Eric Elliott - October 17, 2013

        Usually you won’t have to worry about memory leaks in JavaScript. However, if you have big functions with multiple nested functions inside them, memory leaks can creep in. Check out the blog post A Surprising JavaScript Memory Leak Found at Meteor for a good summary.

        That said, stampit actually makes it easier to avoid such memory leaks, because it isolates everything you need to create complex objects into individual function calls. Everything you need for .enclose() to work is available to the function as this. You should avoid referencing things defined outside the .enclose() function from within that function (JSLint refers to such variable uses as “outer” variables, and it will report them for you if you run your code through it).

        By avoiding that, you avoid memory leaks, but you also avoid tangling between your object and other objects, so it’s less likely to suffer from side-effects caused by other functions and objects.

        Another great way to cut down on potential memory leaks, and generally make your code more reusable is to remove nested functions from the enclosing function, and instead put them at the root level of your module. In order to share data with them, don’t reference that data from the closure… instead, pass that data into them as parameters. Doing so reduces potential side-effects, and reduces your reliance on the closure to get things done. As a bonus, once everything you need for a function is parameterized, it’s much easier to move the function around and refactor your code.

        I hope that answers your question. Good luck!

    16. Dumitru "Mitic?" Ungureanu - October 25, 2013

      Nitpick.

      @17:13 > get: function(name, value){…}

      Why the value parameter?

      • Eric Elliott - October 27, 2013

        You caught me in a cut-and-paste error. Thanks for pointing it out. =)

    17. Dumitru "Mitica" Ungureanu - October 25, 2013

      …and there’s the troll bit.

      Excluding _, backbone and stampit, the whole concept you present seems to revolve around the “pass my methods by value” or “pass my methods by reference” with a touch of “best use closure for encapsulation”.

      You argue that “pass my methods by value” (extent) is more flexible at the noted expense of: slower object instantiation, more used memory, lost prototype chain. But it’s worth it.

      So far so good?

      • Eric Elliott - October 27, 2013

        the whole concept you present seems to revolve around the “pass my methods by value” or “pass my methods by reference” with a touch of “best use closure for encapsulation”.

        Um, no. Not quite. The “whole concept I present” is using prototypal inheritance rather than classical inheritance for code sharing. The two are very different beasts, and not just in how objects get instantiated, but also what you can practically inherit (you can do things with prototypal OO that are not possible with classical OO), and how objects behave after they have been instantiated: Perhaps most importantly, users of classical inheritance tend to create large, linked inheritance graphs — coupled relationships between child classes and parent classes.

        Using a single delegate prototype (collapsing any other prototypes that are also inherited using concatenative inheritance), and object extension (maybe what you meant by “pass my methods by value?”), no such coupled relationships exist. I’d also like to point out that the performance differences for both object instantiation and property access can be written to make either classical inheritance with constructors, or prototypal inheritance with Object.create() appear faster, depending on how you initialize state for the newly created objects. For example, if you assign to properties one at a time in a constructor function, and assign all the properties at once using an object literal in a factory, the factory will outperform the constructor.

        With either method, you’re looking at around 100,000 ops/sec performance for the slowest performer on a typical modern computer. Property access will clock it at millions of ops/sec with either method, and again, if you have a deep classical inheritance dependency hierarchy, and a shallow single delegate prototypal instance, the prototypal instance will win for most cases. There are tiny micro optimizations in engines like V8 that can tip the scales in either direction depending on how you test the benchmarks.

        In other words, if you let performance decide for you, you’re on a fool’s errand, and “You’re Optimizing the Wrong Things”.

        Also, delegate prototypes don’t use more memory than classical inheritance (which also uses delegate prototypes). You can perform concatenative inheritance on the delegate prototype, a technique that the Stampit library uses for .methods() — meaning that if you inherit from a factory, that inherits from another factory, that inherits from another factory, it has a better memory profile than the equivalent object constructed using classical inheritance and a constructor function, because there’s only one delegate, and no overridden properties in the prototype chain.

        As for the “lost prototype chain” — the prototype chain is just a series of links that the JS engine follows to find the property you’re trying to look up. If the property exists on the instance, or the single ancestor delegate, it’s still going to find your property, which means you haven’t lost anything of value.

        That’s all you should care about as a programmer. It’s my argument that if you’re relying on instanceof or .isPrototypeOf(), you’re doing it wrong. First, those methods of detection are not reliable across execution contexts — a very important consideration in JavaScript, especially with iFrames, Web Components, proxy objects, etc…, and you’re missing out on the benefits of duck typing and generic functions — some of the best features of JavaScript.

        So yeah, it’s worth it, because none of the drawbacks you mentioned actually exist in any practical way.

    18. Dumitru "Mitic?" UNGUREANU - October 29, 2013

      OK, thanks.

    19. Its possible with Stampit access to a method/property that are inside on other object?

      Editing your basic example:

      var b = stampit().enclose(function () {
        var a = 'b';
        this.getB = function () {
          return a + this.getA(); //Trying to access to "a" object
        };

      });

      • Eric Elliott - December 2, 2013

        Sure, just used your exposed getters and setters as you’ve done there. Try it and see. =)

    20. Hi Eric,

      I’ve read many of your articles related to JS inheritance, etc., your related “Fluent talk” and I’m basically reading everything related to JS, including discussions on “new” VS “not new”. And as far as I’m trying to be open minded I don’t still get it. I think, as many mentioned above, I’ll probably need some real-life examples to prove the point. So far, to me, major differences are simply coding style and conventions. And that’s fine, everyone has different taste …

      For example, your example with video player, quite contrary showed to me, that you don’t really need your approach. You can as well instantiate and compose “new FlashPlayer”, or “new HTMLPlayer” inside VideoPlayer factory. But then I don’t further need “FlashPlayer” to be another factory itself. Maybe it’s more semantic argument for me …

      Anyway, I’m still comfortable using “new”, but I’m not saying your approach is bad. I’m trying to be open minded and maybe I just need some really good example for the “aha” effect.

      Ales

      • Eric Elliott - February 12, 2014

        Ales, there’s more to it than that. For example, with classical inheritance, it’s not possible (with any currently existing library I’m aware of) to inherit from multiple sources and inherit their privileged methods and private data. To do that, you have to combine classical inheritance with prototypal techniques like concatenation and functional inheritance. The Fluent talk gave plenty of info about the trouble you can get into with classical inheritance hierarchies — the Gorilla / Banana problem is particularly troubling. Because of the lack of selective inheritance, the Diamond problem can be problematic when you rely on class to power your inheritance. Eventually, all inheritance hierarchies are “wrong” for new use cases.

        Basically, if you start with classical inheritance, there is no way to get around it: you’re going to have to mix inheritance techniques in illogical inconsistent ways. For a real example of that, Node’s streams use util.inherits for inheritance, which is basically a functional application of pseudo-classical inheritance. There are readable streams, writeable streams, and duplex streams which inherit from both readable and writable. Since util.inherits uses a single parent classical model, multiple inheritance situations require developers to select different inheritance techniques in order to inherit from multiple ancestors. It’s inconsistent and confusing, but worse, the techniques you use to inherit from readable and writable don’t work like you’d expect with duplex streams. You have to jump through some painful hoops to inherit from duplex. This problem is exasperating enough that there are a number of libraries in the node ecosystem which exist solely to make it easier to create duplex streams (see through, for example).

        So, while it may seem to you that I’m just talking about stylistic preferences, I assure you, there’s more to it than that, and prototypal techniques allow flexibility and consistency that is impossible with the classical model. The Objects chapter in my book might be useful. In particular, check out what you can do with Stampit. None of the class libraries (or the class keyword coming to ECMAScript 6) can match its capabilities.

        • Eric,
          I agree with you that the classical inheritance has its limits, which you find quickly if you need more than one level of subclasses; I’ve been there.

          Actually, with one of my recent projects, I was getting into more complex inheritance/composition design and started to appreciate the different kind of inheritance. So I think I’ll give Stamp and your approach another look. And I’ll definitely check your book when its out!

          Ales

    Add Comment Register



    Leave a Reply

    Your email address will not be published. Required fields are marked *

    You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">