Javascript: Inheritance (from the Good Parts)

I’m taking part in an ACCU study group based around the book JavaScript: The Good Parts by Douglas Crockford. It fell to me to review Chapter 5, which is on Inheritance. It’s a short chapter, but pretty dense, and it took a while to get my head around. Here’s what I thought…


This chapter starts by introducing prototypal inheritance, where objects inherit directly from other classes. This is less familiar to most of us than classical inheritance, where objects are instances of classes which then inherit from other classes.

(I'm forever stumbling on the pronunciation of prototypal. Prototypical just seems more natural to me. To try and get over it I followed the Alyssa P. Hacker pattern from SICP and started saying Prototype Al in my head. It made it worse. I still stumble on Prototype Al, but now I stumble on classical too: Classic Al makes me think of The Fonz.)

The author compares the use of inheritance in classical languages and JavaScript. He asserts that inheritance provides two services in classical languages: code reuse and type-hierachy. As JavaScript is loosely typed the type-hierarchy use is redundant, leaving only code reuse. This left me a little uneasy, as inheritance for code reuse is generally discredited in classical languages. I've learned to prefer composition over inheritance. What we learn here is that JavaScript's loosely-typed, prototypal behaviour is sufficiently different from classical languages to support many different inheritance and code reuse patterns.

I'm tempted to remind myself why we prefer composition over inheritance, and to check how the reasoning is different for JavaScript. Is it different because of differences of typing, because of prototype-based programming, or both? Oh, and any ideas what the author means by loose typing here? I'm somewhat familiar with static vs dynamic typing, and strong vs weak typing. Where does loose typing fit in?

Pseudoclassical

Pseudoclassical is the first inheritance pattern we're shown. With the constructor invocation with new, and the constructor property that's set on every Function object's prototype, it's somewhat built in to the language. That doesn't mean it's the best pattern. We're shown that it's something like a confused attempt to make prototypal inheritance look like that of classical languages.

I found parts of this section hard work, I think mainly due to confusion about whether the word object refers to the constructor function (which, like everything else, is an object) or to the new object we hope to construct with it. After a few reads I think I managed to keep it straight, and the example helped.

Another confusion I had was thinking that every object has a prototype property. All objects have an internal reference to a prototype object, but it's not accessible directly. A Function object has both this internal reference to its prototype, and a prototype property. Its prototype property doesn't point to its own prototype, but to the object that will be set as the internal prototype of any objects constructed by using new with the function. Confused yet? See the diagram here:

Uses of prototype
Uses of prototype

There's one snippet of code in the example on page 47 that seems too tricky to me:

return (typeof other === 'object' && other) || that;

The mixture of types in that expression just seems odd:

(boolean && object) || object

Am I being too classical when I judge that as a misuse of short-circuit operators? I'd have thought that

return typeof other === 'object' ? other : that;

is better all round, or have I missed something?


Edited Nov 27, 2012 @ 10:26

Stewart Brodie quickly pointed out on the group’s mailing list that I had in-fact missed something:

if ‘other’ is null, your code will return null rather than ‘that‘.

That particular logic branch was the whole reason I was suspicious of the snippet, but I wrongly assumed that an object where typeof other === 'object' is true yet other was falsy was impossible, thus making the code to handle it an unnecessary distraction. I failed to consider null.

Note to self: null and undefined means my old 3-valued logic must become 4-valued for JavaScript.)


Finally we're told about a serious design error in the language. Fail to use new when you invoke a constructor function and this will be set to the global object, and so it stomps over names in the global scope rather than creating a fresh object. Despite a convention of using an initial capital letter for, and only for, constructor functions, this is enough of a problem to consign this pattern to the Bad Parts of JavaScript. There are better code reuse alternatives we should use.

Prototypal

The first alternative is Prototypal inheritance. It completely ignores the invocation expression with new, and relegates the use of constructor functions (with an initial capital) to an implementation detail of Object.create (which was introduced in Chapter 3). As such, we never type prototype in our own code. Instead, our (lowercase initial letter)
constructors are just regular functions that return objects.

Functional

The final alternative is Functional inheritance. If I've understood it correctly it's just Prototypal inheritance extended with the Module techniques we saw in Chapter 4 to make the object's innards private: until now they've been public for all to see, play with, and break.

A template for a functional constructor is shown which allows for truly private variables and functions together with variables and functions that are shared with other constructors in the inheritance chain via an argument named, presumably by convention, my. my brings to mind protected visibility in classical languages, but it's more than that: not only can the child constructor see its parents my bits, we could also arrange for the parent to see the child's my bits. None of the examples use my and I'm not clear on the best way to make use of it, so I'll be looking out for it in future.

Another thing I think is worth highlighting is a trick that accesses one public method from another via a private variable. That way, even if a malicious/clueless user replaces the first public method on the resulting object, the other methods that use it will continue to access the original method. The techniques shown prevent your object's innards leaking out even if a user tries to replace its methods with spy functions. They can break your objects but they can't get at your secrets. The author calls an object that is so protected durable, and says that each method on such an object is a capability. I've not heard those terms before, and I would be interested to learn if they're terms from previous literature or are inventions of the author.

Other bits

Elsewhere the chapter talks about object specifiers as a way to make constructor arguments friendlier: they effectively provide named parameters with defaults. The final section talks about a technique for composing the behaviour of an object from a set of Parts. We've already seen how we can add a method to any object. Parts extend this to a function which adds a related set of methods that share state amongst themselves. I don't know a lot about Mixins, but it looks very similar.

Conclusion

This chapter did a good job of showing the problems with the naive approach to inheritance that a classical programmer might take. It then demonstrates some alternatives, working up to Functional pattern, which feels like it should be the preferred option for creating new objects. The author points out that more complicated constructions are possible, but it seems like this pattern is aimed at a sweet spot between encapsulation, reuse and complexity. It feels like a good foundation for starting to work with objects in JavaScript. I once tried to understand how jQuery was put together and failed. I feel much better equipped now.

Some of the comments on classical programming seem odd, and in particular this sentence is wrong: "In classical languages, class inheritance is the only form of code reuse", and so I'm not sure what to make of the comparisons made between JavaScript and other languages. That said, I'm not reading this book to learn classical programming. As a JavaScript novice I can't really comment on the accuracy of the JavaScript advice, but it seems solid enough.

Leave a Reply

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