|
1234567 |
- title: Common Misconceptions About Inheritance in JavaScript
- url: https://medium.com/javascript-scene/common-misconceptions-about-inheritance-in-javascript-d5d9bab29b0a
- hash_url: 96f3df2f689eb35d5e5e551ab63088f8
-
- <h3 name="5b94" id="5b94" class="graf--h3 graf--first">Aren’t classical inheritance and prototypal inheritance really the same thing, just a stylistic preference?</h3><blockquote name="a737" id="a737" class="graf--pullquote pullquote"><strong class="markup--strong markup--pullquote-strong">No.</strong></blockquote><p name="3db3" id="3db3" class="graf--p">Classical and prototypal inheritance are <strong class="markup--strong markup--p-strong">fundamentally and semantically distinct.</strong></p><p name="b561" id="b561" class="graf--p">There are some <strong class="markup--strong markup--p-strong">defining characteristics</strong> between classical inheritance and prototypal inheritance. For any of this article to make sense, you must keep these points in mind:</p><p name="9e70" id="9e70" class="graf--p">In <strong class="markup--strong markup--p-strong">class inheritance, instances inherit from a blueprint</strong> (the class), and <strong class="markup--strong markup--p-strong">create sub-class relationships</strong>. In other words, you can’t use the class like you would use an instance. <strong class="markup--strong markup--p-strong">You can’t invoke instance methods on a class definition itself.</strong> You must first create an instance and then invoke methods on that instance.</p><p name="007b" id="007b" class="graf--p">In prototypal inheritance, <strong class="markup--strong markup--p-strong">instances inherit from other instances. </strong>Using <strong class="markup--strong markup--p-strong">delegate prototypes</strong> (setting the prototype of one instance to refer to an <strong class="markup--strong markup--p-strong">examplar object</strong>), it’s literally <strong class="markup--strong markup--p-strong">Objects Linking to Other Objects</strong>, or <strong class="markup--strong markup--p-strong">OLOO</strong>, as Kyle Simpson calls it. Using <strong class="markup--strong markup--p-strong">concatenative inheritance</strong>, you just <strong class="markup--strong markup--p-strong">copy properties</strong> from an <strong class="markup--strong markup--p-strong">exemplar object</strong> to a new instance.</p><p name="1588" id="1588" class="graf--p">It’s really important that you understand these differences. Class inheritance by virtue of its mechanisms <strong class="markup--strong markup--p-strong">create class hierarchies as a side-effect of sub-class creation.</strong> Those hierarchies lead to <strong class="markup--strong markup--p-strong">arthritic code</strong> (hard to change) and <strong class="markup--strong markup--p-strong">brittleness</strong> (easy to break due to rippling side-effects when you modify base classes).</p><p name="fdfd" id="fdfd" class="graf--p">Prototypal inheritance does not necessarily create similar hierarchies. I recommend that you keep prototype chains as shallow as possible. It’s easy to flatten many prototypes together to form a <strong class="markup--strong markup--p-strong">single delegate prototype.</strong></p><p name="010c" id="010c" class="graf--p">TL;DR:</p><ul class="postList"><li name="ea51" id="ea51" class="graf--li">A <strong class="markup--strong markup--li-strong">class </strong>is a <strong class="markup--strong markup--li-strong">blueprint</strong>.</li><li name="9b8b" id="9b8b" class="graf--li">A <strong class="markup--strong markup--li-strong">prototype</strong> is an <strong class="markup--strong markup--li-strong">object</strong> instance.</li></ul><p name="3900" id="3900" class="graf--p graf--empty"><br/></p><h3 name="7dea" id="7dea" class="graf--h3">Aren’t classes the right way to create objects in JavaScript?</h3><blockquote name="7676" id="7676" class="graf--pullquote pullquote"><strong class="markup--strong markup--pullquote-strong">No.</strong></blockquote><p name="308b" id="308b" class="graf--p">There are several <strong class="markup--strong markup--p-strong">right ways</strong> to create objects in JavaScript. The first and most common is an object literal. It looks like this (in ES6):</p><figure name="22d1" id="22d1" class="graf--figure graf--iframe"><p class="iframeContainer"/></figure><p name="a6f1" id="a6f1" class="graf--p">Of course, object literals have been around a lot longer than ES6, but they lack the method shortcut seen above, and you have to use <em class="markup--em markup--p-em">`var`</em> instead of <em class="markup--em markup--p-em">`let`</em>. Oh, and that template string thing in the <em class="markup--em markup--p-em">`.describe()`</em> method won’t work in ES5, either.</p><p name="281b" id="281b" class="graf--p">You can attach delegate prototypes with <em class="markup--em markup--p-em">`Object.create()`</em> (an ES5 feature):</p><figure name="b75a" id="b75a" class="graf--figure graf--iframe"><p class="iframeContainer"/></figure><p name="779b" id="779b" class="graf--p">Let’s break this one down a little. <em class="markup--em markup--p-em">`animal`</em> is a <strong class="markup--strong markup--p-strong">delegate prototype</strong>. <em class="markup--em markup--p-em">`mouse`</em> is an instance. When you try to access a property on <em class="markup--em markup--p-em">`mouse` </em>that isn’t there, the JavaScript runtime will look for the property on <em class="markup--em markup--p-em">`animal`</em> (the delegate).</p><p name="3b5c" id="3b5c" class="graf--p"><strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">`Object.assign()`</em></strong> is a new ES6 feature championed by Rick Waldron that was previously implemented in a few dozen libraries. You might know it as <em class="markup--em markup--p-em">`$.extend()`</em> from jQuery or <em class="markup--em markup--p-em">`_.extend()`</em> from Underscore. Lodash has a version of it called <em class="markup--em markup--p-em">`assign()`. </em>You pass in a destination object, and as many source objects as you like, separated by commas. It will copy all of the <strong class="markup--strong markup--p-strong">enumerable own properties</strong> by <em class="markup--em markup--p-em">assignment</em> from the source objects to the destination objects with <strong class="markup--strong markup--p-strong">last in priority. </strong>If there are any property name conflicts, the version from the last object passed in wins.</p><p name="3182" id="3182" class="graf--p"><strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">`Object.create()`</em></strong> is an ES5 feature that was championed by Douglas Crockford so that we could attach delegate prototypes without using constructors and the <em class="markup--em markup--p-em">`new`</em> keyword.</p><p name="1398" id="1398" class="graf--p">I’m skipping the constructor function example because I can’t recommend them. I’ve seen them abused a lot, and I’ve seen them cause <a href="https://medium.com/javascript-scene/how-to-fix-the-es6-class-keyword-2d42bb3f4caf" data-href="https://medium.com/javascript-scene/how-to-fix-the-es6-class-keyword-2d42bb3f4caf" class="markup--anchor markup--p-anchor">a lot of trouble</a>. It’s worth noting that a lot of smart people disagree with me. Smart people will do whatever they want.</p><p name="6ab7" id="6ab7" class="graf--p">Wise people will <strong class="markup--strong markup--p-strong">take Douglas Crockford’s advice:</strong></p><blockquote name="0516" id="0516" class="graf--blockquote graf--startsWithDoubleQuote">“If a feature is sometimes dangerous, and there is a better option, then always use the better option.”</blockquote><p name="5b0a" id="5b0a" class="graf--p graf--empty"><br/></p><h3 name="1be0" id="1be0" class="graf--h3">Don’t you need a constructor function to specify object instantiation behavior and handle object initialization?</h3><blockquote name="94ac" id="94ac" class="graf--pullquote pullquote"><strong class="markup--strong markup--pullquote-strong">No.</strong></blockquote><p name="a551" id="a551" class="graf--p">Any function can create and return objects. When it’s not a constructor function, it’s called a <strong class="markup--strong markup--p-strong">factory function.</strong></p><h4 name="020a" id="020a" class="graf--h4">The Better Option</h4><figure name="39dd" id="39dd" class="graf--figure graf--iframe"><p class="iframeContainer"/></figure><p name="122a" id="122a" class="graf--p">I usually don’t name my factories “factory” — that’s just for illustration. Normally I just would have called it <em class="markup--em markup--p-em">`mouse()`.</em></p><p name="79ff" id="79ff" class="graf--p graf--empty"><br/></p><h3 name="b76c" id="b76c" class="graf--h3">Don’t you need constructor functions for privacy in JavaScript?</h3><blockquote name="2e4b" id="2e4b" class="graf--pullquote pullquote"><strong class="markup--strong markup--pullquote-strong">No.</strong></blockquote><p name="239a" id="239a" class="graf--p">In JavaScript, any time you export a function, that function has access to the outer function’s variables. When you use them, the JS engine creates a <strong class="markup--strong markup--p-strong">closure</strong>. Closures are a common pattern in JavaScript, and they’re commonly used for data privacy.</p><p name="0d1d" id="0d1d" class="graf--p">Closures are not unique to constructor functions. Any function can create a closure for data privacy:</p><figure name="9675" id="9675" class="graf--figure graf--iframe"><p class="iframeContainer"/></figure><figure name="c55d" id="c55d" class="graf--figure"><div class="aspectRatioPlaceholder is-locked"><p class="aspect-ratio-fill"/><img class="graf-image" data-image-id="1*PZMdDrJu--I-WSKbrJL7Nw.jpeg" data-width="640" data-height="537" src="https://d262ilb51hltx0.cloudfront.net/max/800/1*PZMdDrJu--I-WSKbrJL7Nw.jpeg"/></div></figure><p name="bcc9" id="bcc9" class="graf--p graf--empty"><br/></p><h3 name="2969" id="2969" class="graf--h3">Does `new` mean that code is using classical inheritance?</h3><blockquote name="7358" id="7358" class="graf--pullquote pullquote"><strong class="markup--strong markup--pullquote-strong">No.</strong></blockquote><p name="6db2" id="6db2" class="graf--p">The `<em class="markup--em markup--p-em">new`</em> keyword is used to invoke a constructor. What it actually does is:</p><ul class="postList"><li name="64cf" id="64cf" class="graf--li">Create a new instance</li><li name="bc9c" id="bc9c" class="graf--li">Bind `<em class="markup--em markup--li-em">this`</em> to the new instance</li><li name="6b68" id="6b68" class="graf--li">Reference the new object’s delegate [[Prototype]] to the object referenced by the constructor function’s <em class="markup--em markup--li-em">`prototype`</em> property.</li><li name="46ad" id="46ad" class="graf--li">Names the object type after the constructor, which you’ll notice mostly in the debugging console. You’ll see `<em class="markup--em markup--li-em">[Object Foo]`,</em> for example, instead of `<em class="markup--em markup--li-em">[Object object]`.</em></li><li name="06b5" id="06b5" class="graf--li">Allows `<em class="markup--em markup--li-em">instanceof`</em> to check whether or not an object’s prototype reference is the same object referenced by the <em class="markup--em markup--li-em">.prototype</em> property of the constructor.</li></ul><h4 name="b7d3" id="b7d3" class="graf--h4"><em class="markup--em markup--h4-em">`instanceof`</em> lies</h4><p name="e697" id="e697" class="graf--p graf--empty"><br/></p><p name="0484" id="0484" class="graf--p">Let’s pause here for a moment and reconsider the value of <em class="markup--em markup--p-em">`instanceof`</em>. You might change your mind about its usefulness.</p><p name="6f8c" id="6f8c" class="graf--p"><strong class="markup--strong markup--p-strong">Important:</strong> `<em class="markup--em markup--p-em">instanceof`</em> does not do type checking the way that you expect similar checks to do in strongly typed languages. Instead, it does an identity check on the prototype object, and it’s easily fooled. It won’t work across execution contexts, for instance (a common source of bugs, frustration, and unnecessary limitations). For reference, an <a href="https://github.com/baconjs/bacon.js/issues/296" data-href="https://github.com/baconjs/bacon.js/issues/296" class="markup--anchor markup--p-anchor" rel="nofollow">example in the wild, from bacon.js</a>.</p><p name="7565" id="7565" class="graf--p">It’s also easily tricked into false positives (and more commonly) false negatives from another source. Since it’s an identity check against a target object’s `<em class="markup--em markup--p-em">.prototype`</em> property, it can lead to strange things:</p><pre name="597b" id="597b" class="graf--pre">> function foo() {}<br/>> var bar = { a: ‘a’};<br/>> foo.prototype = bar; // Object {a: “a”}<br/>> baz = Object.create(bar); // Object {a: “a”}<br/>> baz instanceof foo // true. oops.</pre><p name="b4e1" id="b4e1" class="graf--p">That last result is completely in line with the JavaScript specification. Nothing is broken — it’s just that <em class="markup--em markup--p-em">`instanceof`</em> can’t make any guarantees about type safety. <strong class="markup--strong markup--p-strong">It’s easily tricked</strong> into reporting both <strong class="markup--strong markup--p-strong">false positives</strong>, and <strong class="markup--strong markup--p-strong">false negatives</strong>.</p><p name="3a80" id="3a80" class="graf--p">Besides that, trying to force your JS code to behave like strongly typed code can block your functions from being lifted to generics, which are much more reusable and useful.</p><p name="b6c7" id="b6c7" class="graf--p graf--last"><strong class="markup--strong markup--p-strong">`<em class="markup--em markup--p-em">instanceof</em>` limits the reusability of your code, and potentially introduces bugs into the programs that use your code.</strong></p>
- <blockquote name="fea2" id="fea2" class="graf--pullquote pullquote graf--first">`instanceof` lies.</blockquote><blockquote name="b9a1" id="b9a1" class="graf--pullquote pullquote graf--last">Ducktype instead.</blockquote></div></div></section><section name="a7c1" class=" section--content"><div class="section-divider layoutSingleColumn"><hr class="section-divider"></div><div class="section-content"><div class="section-inner layoutSingleColumn"><h4 name="a10b" id="a10b" class="graf--h4 graf--first">`new` is weird</h4><p name="e4fa" id="e4fa" class="graf--p graf--empty"><br></p><p name="ec06" id="ec06" class="graf--p"><strong class="markup--strong markup--p-strong">WAT?</strong> `<em class="markup--em markup--p-em">new</em>` also does some <em class="markup--em markup--p-em">weird stuff</em> to return values. If you try to return a primitive, it won’t work. If you return any other arbitrary object, that <strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">does</em></strong> work, but <em class="markup--em markup--p-em">`this`</em> gets thrown away, breaking all references to it (including `<em class="markup--em markup--p-em">.call()`</em> and `<em class="markup--em markup--p-em">.apply()`</em>), and breaking the link to the constructor’s <em class="markup--em markup--p-em">`.prototype`</em> reference.</p><h4 name="646d" id="646d" class="graf--h4 graf--empty"><br></h4><p name="01d4" id="01d4" class="graf--p graf--empty"><br></p><h3 name="68b2" id="68b2" class="graf--h3">Is There a Big Performance Difference Between Classical and Prototypal Inheritance?</h3><blockquote name="422f" id="422f" class="graf--pullquote pullquote"><strong class="markup--strong markup--pullquote-strong">No.</strong></blockquote><p name="febe" id="febe" class="graf--p">You may have heard of <strong class="markup--strong markup--p-strong">hidden classes, </strong>and think that constructors dramatically outperform objects instantiated with <em class="markup--em markup--p-em">`Object.create()`</em>. Those performance differences are <strong class="markup--strong markup--p-strong">dramatically overstated.</strong></p><p name="e5e4" id="e5e4" class="graf--p">A small fraction of your application’s time is spent running JavaScript, and a miniscule fraction of that time is spent accessing properties on objects. In fact, the slowest laptops being produced today can access <em class="markup--em markup--p-em">millions of properties per second.</em></p><p name="4956" id="4956" class="graf--p"><strong class="markup--strong markup--p-strong">That’s not your app’s bottleneck</strong>. Do yourself a favor and <a href="http://www.paulirish.com/2015/advanced-performance-audits-with-devtools/" data-href="http://www.paulirish.com/2015/advanced-performance-audits-with-devtools/" class="markup--anchor markup--p-anchor" rel="nofollow">profile your app</a> to <strong class="markup--strong markup--p-strong">discover your real performance bottlenecks</strong>. I’m sure there are a million things you should fix before you spend another moment thinking about micro-optimizations.</p><p name="e51a" id="e51a" class="graf--p"><strong class="markup--strong markup--p-strong">Not convinced?</strong> For a micro-optimization to have any appreciable impact on your app, you’d have to loop over the operation <strong class="markup--strong markup--p-strong">hundreds of thousands of times</strong>, and the only differences in micro-optimization you should ever be concerned about are the ones that are <strong class="markup--strong markup--p-strong">orders of magnitude apart</strong>.</p><p name="6229" id="6229" class="graf--p"><strong class="markup--strong markup--p-strong">Rule of thumb:</strong> Profile your app and eliminate as many loading, networking, file I/O, and rendering bottlenecks as you can find. <strong class="markup--strong markup--p-strong">Then and only then should you start to think about a micro-optimization.</strong></p><p name="fb32" id="fb32" class="graf--p">Can you tell the difference between <em class="markup--em markup--p-em">.0000000001</em> seconds and <em class="markup--em markup--p-em">.000000001</em> seconds? Neither can I, but I sure can tell the difference between loading 10 small icons or loading one web font, instead!</p><p name="06a6" id="06a6" class="graf--p">If you do <strong class="markup--strong markup--p-strong">profile your app</strong> and find that object creation really is a bottleneck, the fastest way to do it is not by using <em class="markup--em markup--p-em">`new`</em> and classical OO. <strong class="markup--strong markup--p-strong">The fastest way is to use object literals</strong>. You can do so in-line with a loop and add objects to an object pool to avoid thrashing from the garbage collector. If it’s worth abandoning prototypal OO over perf, it’s worth ditching the prototype chain and inheritance altogether to crank out object literals.</p><blockquote name="c3f4" id="c3f4" class="graf--blockquote">But Google said class is fast…</blockquote><p name="c9c4" id="c9c4" class="graf--p"><strong class="markup--strong markup--p-strong">WAT? </strong>Google is building a JavaScript engine. You are building an application. Obviously what they care about and what you care about should be <strong class="markup--strong markup--p-strong">very different things</strong>. Let Google handle the micro-optimizations. You worry about <strong class="markup--strong markup--p-strong">your app’s real bottlenecks</strong>. I promise, you’ll get a whole lot better ROI focusing on just about anything else.</p><p name="6bf1" id="6bf1" class="graf--p graf--empty"><br></p><h3 name="dec9" id="dec9" class="graf--h3">Is There a Big Difference in Memory Consumption Between Classical and Prototypal?</h3><blockquote name="74f0" id="74f0" class="graf--pullquote pullquote"><strong class="markup--strong markup--pullquote-strong">No.</strong></blockquote><p name="5898" id="5898" class="graf--p">Both can use delegate prototypes to share methods between many object instances. Both can use or avoid wrapping a bunch of state into closures.</p><p name="5884" id="5884" class="graf--p">In fact, if you start with factory functions, it’s easier to switch to object pools so that you can manage memory more carefully and avoid being blocked periodically by the garbage collector. For more on why that’s awkward with constructors, see the <strong class="markup--strong markup--p-strong">WAT?</strong> note under <em class="markup--em markup--p-em">“Does `new` mean that code is using classical inheritance?”</em></p><p name="dba2" id="dba2" class="graf--p graf--last">In other words, if you want the most flexibility for memory management, use factory functions instead of constructors and classical inheritance.</p></div></div></section><section name="b654" class=" section--content"><div class="section-divider layoutSingleColumn"><hr class="section-divider"></div><div class="section-content"><div class="section-inner layoutSingleColumn"><blockquote name="e3a3" id="e3a3" class="graf--pullquote pullquote graf--startsWithDoubleQuote graf--first graf--last">“…if you want the most flexibility for memory management,<br>use factory functions…”</blockquote></div></div></section><section name="b1f4" class=" section--content"><div class="section-divider layoutSingleColumn"><hr class="section-divider"></div><div class="section-content"><div class="section-inner layoutSingleColumn"><p name="33b0" id="33b0" class="graf--p graf--empty graf--first"><br></p><h3 name="8ed9" id="8ed9" class="graf--h3">The Native APIs use Constructors. Aren’t they More Idiomatic than Factories?</h3><p name="8d10" id="8d10" class="graf--p graf--empty"><br></p><blockquote name="1d8b" id="1d8b" class="graf--pullquote pullquote"><strong class="markup--strong markup--pullquote-strong">No.</strong></blockquote><p name="74d0" id="74d0" class="graf--p"><strong class="markup--strong markup--p-strong">Factories are extremely common in JavaScript.</strong> For instance, the most popular JavaScript library of all time, <strong class="markup--strong markup--p-strong">jQuery</strong> exposes a factory to users. John Resig has written about the choice to use a factory and prototype extension rather than a class. Basically, it boils down to the fact that he didn’t want callers to have to type <em class="markup--em markup--p-em">`new`</em> every time they made a selection. What would that have looked like?</p><figure name="5c35" id="5c35" class="graf--figure graf--iframe"><div class="iframeContainer"><iframe width="700" height="250" src="/media/1efd18420d6f00481898912c65cb97a9?maxWidth=700" data-media-id="1efd18420d6f00481898912c65cb97a9" frameborder="0"></iframe></div></figure><p name="bb8c" id="bb8c" class="graf--p">What else exposes factories?</p><ul class="postList"><li name="b663" id="b663" class="graf--li"><strong class="markup--strong markup--li-strong">React</strong> <em class="markup--em markup--li-em">`React.createClass()`</em> is a factory.</li><li name="49ac" id="49ac" class="graf--li"><strong class="markup--strong markup--li-strong">Angular</strong> uses classes & factories, but wraps them all with a factory in the Dependency Injection container. All providers are sugar that use the <em class="markup--em markup--li-em">`.provider()`</em> factory. There’s even a `.<em class="markup--em markup--li-em">factory()`</em> provider, and even the <em class="markup--em markup--li-em">`.service()`</em> provider wraps normal constructors and exposes … you guessed it: A <strong class="markup--strong markup--li-strong">factory</strong> for DI consumers.</li><li name="ce89" id="ce89" class="graf--li"><strong class="markup--strong markup--li-strong">Ember</strong> <em class="markup--em markup--li-em">`Ember.Application.create();`</em> is a factory that produces the app. Rather than creating constructors to call with <em class="markup--em markup--li-em">`new`</em>, the <em class="markup--em markup--li-em">`.extend()`</em> methods augment the app.</li><li name="69bc" id="69bc" class="graf--li"><strong class="markup--strong markup--li-strong">Node</strong> core services like <em class="markup--em markup--li-em">`http.createServer()`</em> and <em class="markup--em markup--li-em">`net.createServer()` </em>are factory functions.</li><li name="4854" id="4854" class="graf--li"><strong class="markup--strong markup--li-strong">Express</strong> is a factory that creates an express app.</li></ul><p name="884a" id="884a" class="graf--p">As you can see, virtually all of the most popular libraries and frameworks for JavaScript make heavy use of factory functions. <strong class="markup--strong markup--p-strong">The only object instantiation pattern more common than factories in JS is the object literal.</strong></p><p name="ada9" id="ada9" class="graf--p graf--last">JavaScript built-ins started out using constructors because Brendan Eich was told to make it look like Java. JavaScript continues to use constructors for self-consistency. It would be awkward to try to change everything to factories and deprecate constructors now.</p></div></div></section><section name="9bed" class=" section--content"><div class="section-divider layoutSingleColumn"><hr class="section-divider"></div><div class="section-content"><div class="section-inner layoutSingleColumn"><blockquote name="92cc" id="92cc" class="graf--pullquote pullquote graf--first graf--last">That doesn’t mean that <strong class="markup--strong markup--pullquote-strong">your APIs</strong> have to suck.</blockquote></div></div></section><section name="8083" class=" section--content"><div class="section-divider layoutSingleColumn"><hr class="section-divider"></div><div class="section-content"><div class="section-inner layoutSingleColumn"><p name="bc05" id="bc05" class="graf--p graf--empty graf--first"><br></p><h3 name="0eeb" id="0eeb" class="graf--h3">Isn’t Classical Inheritance More Idiomatic than Prototypal Inheritance?</h3><blockquote name="9405" id="9405" class="graf--pullquote pullquote"><strong class="markup--strong markup--pullquote-strong">No.</strong></blockquote><p name="6dd1" id="6dd1" class="graf--p">Every time I hear this misconception I am tempted to say, <strong class="markup--strong markup--p-strong">“do u even JavaScript?”</strong> and move on… but I’ll resist the urge and set the record straight, instead.</p><p name="6faf" id="6faf" class="graf--p">Don’t feel bad if this is your question, too. It’s <strong class="markup--strong markup--p-strong">not your fault</strong>. <a href="https://medium.com/javascript-scene/javascript-training-sucks-284b53666245" data-href="https://medium.com/javascript-scene/javascript-training-sucks-284b53666245" class="markup--anchor markup--p-anchor">JavaScript Training Sucks!</a></p><p name="acc0" id="acc0" class="graf--p">The answer to this question is a big, gigantic</p><h2 name="1e55" id="1e55" class="graf--h2">No… (but)</h2><p name="f63f" id="f63f" class="graf--p graf--empty"><br></p><p name="720c" id="720c" class="graf--p">Prototypes are the idiomatic inheritance paradigm in JS, and <em class="markup--em markup--p-em">`class` </em>is the marauding invasive species.</p><h4 name="0be1" id="0be1" class="graf--h4">A brief history of popular JavaScript libraries:</h4><p name="e062" id="e062" class="graf--p graf--empty"><br></p><p name="b79c" id="b79c" class="graf--p">In the beginning, everybody wrote their own libs, and open sharing wasn’t a big thing. And then <strong class="markup--strong markup--p-strong">Prototype</strong> came along. (The name is a big hint here). Prototype did its magic by extending built-in <strong class="markup--strong markup--p-strong">delegate prototypes</strong> using <strong class="markup--strong markup--p-strong">concatenative inheritance</strong>.</p><p name="caf3" id="caf3" class="graf--p">Later we all realized that modifying built-in prototypes was an anti-pattern when native alternatives and conflicting libs broke the internet. But that’s a different story.</p><p name="3e30" id="3e30" class="graf--p">Next on the JS lib popularity roller coaster was <strong class="markup--strong markup--p-strong">jQuery</strong>. jQuery’s big claim to fame was <strong class="markup--strong markup--p-strong">jQuery plugins</strong>. They worked by extending jQuery’s <strong class="markup--strong markup--p-strong">delegate prototype </strong>using <strong class="markup--strong markup--p-strong">concatenative inheritance.</strong></p><blockquote name="8ac3" id="8ac3" class="graf--pullquote pullquote">Are you starting to sense a pattern here?</blockquote><p name="affe" id="affe" class="graf--p"><strong class="markup--strong markup--p-strong">jQuery remains the most popular JavaScript library ever made. By a HUGE margin. HUGE.</strong></p><p name="44c2" id="44c2" class="graf--p">This is where things get muddled and class extension starts to sneak into the language… John Resig (author of jQuery) wrote about <em class="markup--em markup--p-em">Simple Class Inheritance in JavaScript</em>, and people started <em class="markup--em markup--p-em">actually using it,</em> even though John Resig himself didn’t think it belonged in jQuery (because <strong class="markup--strong markup--p-strong">prototypal OO did the same job better</strong>).</p><p name="717e" id="717e" class="graf--p">Semi-popular Java-esque frameworks like ExtJS appeared, ushering in the first kinda, sorta, not-really mainstream uses of class in JavaScript. This was 2007. JavaScript was <strong class="markup--strong markup--p-strong">12 years old</strong> before a somewhat popular lib started exposing JS users to classical inheritance.</p><p name="cfb5" id="cfb5" class="graf--p">Three years later, Backbone <strong class="markup--strong markup--p-strong">exploded</strong> and had an <em class="markup--em markup--p-em">`.extend()`</em> method that mimicked class inheritance, including all its nastiest features such as brittle object hierarchies. That’s when <em class="markup--em markup--p-em">all hell broke loose.</em></p><blockquote name="0bd0" id="0bd0" class="graf--blockquote">~100kloc app starts using Backbone. A few months in I’m debugging a 6-level hierarchy trying to find a bug. Stepped through every line of constructor code up the `super` chain. Found and fixed the bug in the top level base class. Then had to fix a lot of child classes because they depended on the buggy behavior of the base class. <strong class="markup--strong markup--blockquote-strong">Hours of frustration</strong> that <strong class="markup--strong markup--blockquote-strong">should have been a 5 minute fix</strong>.</blockquote><p name="6f2c" id="6f2c" class="graf--p"><strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">This is not JavaScript.</em></strong> I was suddenly living in <em class="markup--em markup--p-em">Java hell</em> again. That lonely, dark, scary place where any quick movements could cause entire hierarchies to shudder and collapse in coalescing, tight-coupled convulsions.</p><p name="fc83" id="fc83" class="graf--p"><strong class="markup--strong markup--p-strong">These are the monsters rewrites are made of.</strong></p><p name="346f" id="346f" class="graf--p">But, squirreled away in the Backbone docs, a ray of golden sunshine:</p><figure name="a3d9" id="a3d9" class="graf--figure graf--iframe"><div class="iframeContainer"><iframe width="700" height="250" src="/media/7147ee8f2990055bbee5538ffd118419?maxWidth=700" data-media-id="7147ee8f2990055bbee5538ffd118419" frameborder="0"></iframe></div></figure><p name="2b39" id="2b39" class="graf--p">Our old friend, <strong class="markup--strong markup--p-strong">concatenative inheritance</strong> saving the day with a <em class="markup--em markup--p-em">`Backbone.Events`</em> <strong class="markup--strong markup--p-strong">mixin</strong>.</p><p name="e153" id="e153" class="graf--p graf--last">It turns out, if you look at <strong class="markup--strong markup--p-strong">any non-trivial JavaScript library</strong> closely enough, <strong class="markup--strong markup--p-strong">you’re going to find examples of concatenation and delegation</strong>. It’s so common and automatic for JavaScript developers to do these things that <strong class="markup--strong markup--p-strong">they don’t even think of it as inheritance</strong>, even though it <strong class="markup--strong markup--p-strong">accomplishes the same goal.</strong></p></div></div></section><section name="13a5" class=" section--content"><div class="section-divider layoutSingleColumn"><hr class="section-divider"></div><div class="section-content"><div class="section-inner layoutSingleColumn"><blockquote name="93ef" id="93ef" class="graf--pullquote pullquote graf--first">Inheritance in JS is so easy<br>it confuses people who expect it to take effort.</blockquote><blockquote name="ab9a" id="ab9a" class="graf--pullquote pullquote graf--last">To make it harder, we added `class`.</blockquote></div></div></section><section name="987f" class=" section--content"><div class="section-divider layoutSingleColumn"><hr class="section-divider"></div><div class="section-content"><div class="section-inner layoutSingleColumn"><p name="6ca2" id="6ca2" class="graf--p graf--first">And how did we add class? <strong class="markup--strong markup--p-strong">We built it on top of prototypal inheritance</strong> using <strong class="markup--strong markup--p-strong">delegate prototypes</strong> and <strong class="markup--strong markup--p-strong">object concatenation</strong>, of course!</p><p name="5c3d" id="5c3d" class="graf--p"><strong class="markup--strong markup--p-strong">That’s like driving your Tesla Model S to a car dealership and trading it in for a rusted out 1983 Ford Pinto.</strong></p><p name="c7b5" id="c7b5" class="graf--p graf--empty"><br></p><h3 name="88ab" id="88ab" class="graf--h3">Doesn’t the Choice Between Classical and Prototypal Inheritance Depend on the Use Case?</h3><blockquote name="be2b" id="be2b" class="graf--pullquote pullquote"><strong class="markup--strong markup--pullquote-strong">No.</strong></blockquote><p name="e342" id="e342" class="graf--p">Prototypal OO is simpler, more flexible, and a lot less error prone. I have been making this claim and challenging people to come up with a compelling class use case <em class="markup--em markup--p-em">for many years. </em>Hundreds of thousands of people have heard the call. The few answers I’ve received depended on one or more of the misconceptions addressed in this article.</p><p name="d72b" id="d72b" class="graf--p">I was once a classical inheritance fan. I bought into it completely. I built object hierarchies everywhere. I built visual OO Rapid Application Development tools to help software architects design object hierarchies and relationships that made sense. It took a visual tool to truly map and graph the object relationships in enterprise applications using classical inheritance taxonomies.</p><p name="c9c9" id="c9c9" class="graf--p">Soon after my transition from C++ and Java to JavaScript, I stopped doing all of that. Not because I was building less complex apps (the opposite is true), but because JavaScript was so much simpler, I had no more need for all that OO design tooling.</p><p name="d801" id="d801" class="graf--p">I used to do application design consulting and frequently recommend sweeping rewrites. Why? Because <strong class="markup--strong markup--p-strong">all object hierarchies are eventually wrong for new use cases.</strong></p><p name="222b" id="222b" class="graf--p">I wasn’t alone. In those days, <strong class="markup--strong markup--p-strong">complete rewrites</strong> were very common for new software versions. Most of those rewrites were necessitated by legacy lock-in caused by <a href="https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3" data-href="https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3" class="markup--anchor markup--p-anchor">arthritic, brittle class hierarchies</a>. Entire books were written about OO design mistakes and how to avoid them or refactor away from them. It seemed like every developer had a copy of <a href="http://www.amazon.com/gp/product/0201633612?ie=UTF8&camp=213733&creative=393185&creativeASIN=0201633612&linkCode=shr&tag=eejs-20&linkId=QYF6ABRMZ4O6KML2" data-href="http://www.amazon.com/gp/product/0201633612?ie=UTF8&camp=213733&creative=393185&creativeASIN=0201633612&linkCode=shr&tag=eejs-20&linkId=QYF6ABRMZ4O6KML2" class="markup--anchor markup--p-anchor" rel="nofollow">“Design Patterns”</a> on their desk.</p><p name="46f8" id="46f8" class="graf--p">I recommend that you follow the <a href="http://www.amazon.com/gp/product/0201633612?ie=UTF8&camp=213733&creative=393185&creativeASIN=0201633612&linkCode=shr&tag=eejs-20&linkId=QYF6ABRMZ4O6KML2" data-href="http://www.amazon.com/gp/product/0201633612?ie=UTF8&camp=213733&creative=393185&creativeASIN=0201633612&linkCode=shr&tag=eejs-20&linkId=QYF6ABRMZ4O6KML2" class="markup--anchor markup--p-anchor" rel="nofollow">Gang of Four’s advice</a> on this point:</p><blockquote name="317c" id="317c" class="graf--pullquote pullquote graf--startsWithDoubleQuote">“Favor object composition over class inheritance.”</blockquote><p name="480a" id="480a" class="graf--p">In Java, that was harder than class inheritance because you actually had to use classes to achieve it.</p><p name="aeba" id="aeba" class="graf--p">In JavaScript, we don’t have that excuse. It’s actually <strong class="markup--strong markup--p-strong">much easier </strong>in JavaScript to simply create the object that you need by assembling various <strong class="markup--strong markup--p-strong">prototypes</strong> together than it is to manage object hierarchies.</p><p name="c987" id="c987" class="graf--p"><strong class="markup--strong markup--p-strong">WAT? </strong>Seriously. Want the jQuery object that can turn any date input into a <em class="markup--em markup--p-em">`megaCalendarWidget`</em>? You don’t have to<em class="markup--em markup--p-em">`extend`</em> a <em class="markup--em markup--p-em">`class`</em>. JavaScript has dynamic object extension, and jQuery exposes its own prototype so you can just extend that — without an extend keyword! <strong class="markup--strong markup--p-strong">WAT?:</strong></p><figure name="aceb" id="aceb" class="graf--figure graf--iframe"><div class="iframeContainer"><iframe width="700" height="250" src="/media/d46c084f2d3ad2eb9349d367943a141f?maxWidth=700" data-media-id="d46c084f2d3ad2eb9349d367943a141f" frameborder="0"></iframe></div></figure><p name="a856" id="a856" class="graf--p">The next time you call the jQuery factory, you’ll get an instance that can make your date inputs mega awesome.</p><p name="1a02" id="1a02" class="graf--p">Similarly, you can use <em class="markup--em markup--p-em">`Object.assign()` </em>to compose any number of objects together with last-in priority:</p><figure name="2e11" id="2e11" class="graf--figure graf--iframe"><div class="iframeContainer"><iframe width="700" height="250" src="/media/105db7f26614c7111022a5ddc44cb197?maxWidth=700" data-media-id="105db7f26614c7111022a5ddc44cb197" frameborder="0"></iframe></div></figure><p name="26dd" id="26dd" class="graf--p">No, really — <strong class="markup--strong markup--p-strong">any number of objects:</strong></p><figure name="06bd" id="06bd" class="graf--figure graf--iframe"><div class="iframeContainer"><iframe width="700" height="250" src="/media/b35c15e99590b6a785cb577c609675d1?maxWidth=700" data-media-id="b35c15e99590b6a785cb577c609675d1" frameborder="0"></iframe></div></figure><p name="d949" id="d949" class="graf--p">This technique is called <strong class="markup--strong markup--p-strong">concatenative inheritance</strong>, and the prototypes you inherit from are sometimes referred to as <strong class="markup--strong markup--p-strong">exemplar prototypes</strong>, which differ from delegate prototypes in that you <strong class="markup--strong markup--p-strong">copy from them</strong>, rather than delegate to them.</p><p name="1338" id="1338" class="graf--p graf--empty"><br></p><h3 name="ed9d" id="ed9d" class="graf--h3">ES6 Has the `class` keyword. Doesn’t that mean we should all be using it?</h3><blockquote name="8a2b" id="8a2b" class="graf--pullquote pullquote"><strong class="markup--strong markup--pullquote-strong">No.</strong></blockquote><p name="3606" id="3606" class="graf--p">There are lots of <a href="https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3" data-href="https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3" class="markup--anchor markup--p-anchor">compelling reasons</a> to <a href="https://medium.com/javascript-scene/how-to-fix-the-es6-class-keyword-2d42bb3f4caf" data-href="https://medium.com/javascript-scene/how-to-fix-the-es6-class-keyword-2d42bb3f4caf" class="markup--anchor markup--p-anchor">avoid the ES6 <em class="markup--em markup--p-em">`class`</em> keyword</a>, not least of which because it’s an awkward fit for JavaScript.</p><p name="dd38" id="dd38" class="graf--p">We already have an <strong class="markup--strong markup--p-strong">amazingly powerful and expressive object system</strong> in JavaScript. The concept of class as it’s implemented in JS today is more restrictive (in a bad way, not in a cool type-correctness way), and obscures the <a href="http://ericleads.com/2013/02/fluent-javascript-three-different-kinds-of-prototypal-oo/" data-href="http://ericleads.com/2013/02/fluent-javascript-three-different-kinds-of-prototypal-oo/" class="markup--anchor markup--p-anchor" rel="nofollow">very cool prototypal OO system</a> that was built into the language a long time ago.</p><p name="b980" id="b980" class="graf--p">You know what would really be good for JavaScript? Better sugar and abstractions built on top of prototypes <strong class="markup--strong markup--p-strong">from the perspective of a programmer familiar with prototypal OO.</strong></p><p name="d631" id="d631" class="graf--p graf--last">That could be <a href="https://github.com/ericelliott/stampit" data-href="https://github.com/ericelliott/stampit" class="markup--anchor markup--p-anchor" rel="nofollow">really cool</a>.</p>
- </div>
|