
JavaScript: prototype vs. class - Parsyval
https://medium.com/@parsyval/javascript-prototype-vs-class-a7015d5473b
======
drinchev
When comparing ES2015 and how it was before. I usually take the babel
playground [1] and see what the closest alternative would be.

For a simple es2015 it looks a bit more complicated than the prototype
extension [2].

    
    
        class Dog {
          bark() { console.log( "Bark!" ); }
        }
    

Compiles to :

    
    
        "use strict";
    
        var _createClass = (function() {
          function defineProperties(target, props) {
            for (var i = 0; i < props.length; i++) {
              var descriptor = props[i];
              descriptor.enumerable = descriptor.enumerable || false;
              descriptor.configurable = true;
              if ("value" in descriptor) descriptor.writable = true;
              Object.defineProperty(target, descriptor.key, descriptor);
            }
          }
          return function(Constructor, protoProps, staticProps) {
            if (protoProps) defineProperties(Constructor.prototype, protoProps);
            if (staticProps) defineProperties(Constructor, staticProps);
            return Constructor;
          };
        })();
    
        function _classCallCheck(instance, Constructor) {
          if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
          }
        }
    
        var Dog = (function() {
          function Dog() {
            _classCallCheck(this, Dog);
          }
    
          _createClass(Dog, [
            {
              key: "bark",
              value: function bark() {
                console.log("Bark!");
              }
            }
          ]);
    
          return Dog;
        })();
    

1 : [https://babeljs.io/repl/](https://babeljs.io/repl/)

2 :
[https://babeljs.io/repl/#?babili=false&browsers=&build=&buil...](https://babeljs.io/repl/#?babili=false&browsers=&build=&builtIns=false&code_lz=MYGwhgzhAEAiD2BzaBvAUNaAjMAnA1gBQCUq0w8AdhPCAKYB0ISh0ARAEJ74CEb0xANzQAvmhFA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&lineWrap=false&presets=es2015%2Ces2016%2Creact%2Cstage-2&prettier=true&targets=&version=6.26.0&envVersion=)

~~~
Parsyval
Since I work with Typescript, I do the same with the TS playground, it's
really usefull to learn that kind of stuff.

~~~
drinchev
I sometimes see difference between TypeScript's compiler and babel.

For example in this case TypeScript doesn't care so much about
`defineProperty`. I guess the rules are not so strict, since it assumes that
the rest of your code will be passed through TypeScript itself.

Anyway in both cases, I prefer TypeScript's way of enhancing the class
inheritance in JavaScript by introducing private / readonly and many more.

[https://www.typescriptlang.org/play/index.html#src=class%20D...](https://www.typescriptlang.org/play/index.html#src=class%20Dog%20%7B%0D%0A%20%20%20%20bark\(\)%20%7B%0D%0A%20%20%20%20%20%20%20%20console.log\(%22bark%22\)%3B%0D%0A%20%20%20%20%7D%0D%0A%20%20%20%20static%20eat\(\)%20%7B%0D%0A%20%20%20%20%20%20%20%20console.log\(%22boo%22\)%3B%0D%0A%20%20%20%20%7D%20%20%20%0D%0A%7D%0D%0A%0D%0A%0D%0A%0D%0A)

------
lucisferre
While it is good to understand how Javascript prototype inheritance works, I
find it is best avoided , along with `this` and `class` syntactic sugar).

It is rarely required and most anything can be achieved with very basic JS
objects and functions which are simpler to work with, easier to reason about
and easier to write tests for.

~~~
watty
I disagree - not adapting to the latest language paradigms because you find it
more difficult is code smell.

~~~
mjburgess
Code smell describes _code_ which has incurred technical debts among other
things. People do not have it.

The "latest language paradigms" in this case are JavaScript adopting a style
of programming 40+ years old (Simula) -- despite its originating model being
younger (self).

Adopting paradigms _because_ they are "recent additions" to a language is
cargo cultism.

Paradigms come with idioms that suit some problems better than others, and
have all sorts of trade-offs and considerations.

Inheritance is now widely regarded as a design approach, overall, best
avoided.

"easy to reason about" =/= hard to understand. The ease of reasoning about
something is a feature of how complicated it makes it (partly, its incidental
complexity). It's not to do with how dumb you are.

Inhertiance, for example, creates systems that are often needlessly hard to
reason about.

------
callesgg
I like the way javascript does inheritance, it is very convenient most of the
times.

But whenever i start looking on the details of how it actually works, I feel
like my brain is experiencing a bunch of small seizures.

------
hashkb
This doesn't do a good job of supporting its thesis. The new syntax does in
fact allow most devs who understand "traditional" class based inheritance to
use JS the way they expect. The fact that it's implemented in JS is both good
and also OK to ignore for most devs.

~~~
hajile
In my experience, the class syntax has been more bad than good. Devs with
experience in other languages simply start writing classical OOP code without
realizing the ramifications of what they are doing.

Unless you are creating thousands of rigidly structured instances (never
adding/removing properties or changing property types), you are almost always
going to be better off using the factory pattern instead where you get other
benefits like no `new` and real privacy.

~~~
untog
> the ramifications of what they are doing

Which are? And why is "no new" a benefit?

IMO it's a good thing that you can write JS in the way you write other
languages. It makes it a lot easier for developers to get going with it.

~~~
tonyedgecombe
I'm trying to learn modern JavaScript at the moment, as well as getting to
grips with prototypes I now have additional class syntax to learn and
understand.

I've gone from learning one way of doing things to two, how is that easier to
get going with?

~~~
always_good
Because now there's one way instead of everyone bringing their own
createClass/inherits implementation.

Also, prototypical classes like `function User` are now rare in my experience
going forward.

~~~
smokeyj
> Because now there's one way instead of everyone bringing their own

In theory. In reality there's everyone bringing their own plus one more.

I think the appeal of JS is that it's a universal runtime, not anything about
the language itself. I really wish syntactic changes would be left to
transpilation.. because that's what every JS developer is doing anyways.

------
mikegerwitz
Those interested in how prototypes and classical inheritance relate (and
don't) may be interested in work I did on GNU ease.js, which works with
ECMAScript 3+. I have since extended it to support Scala-like traits.

[https://www.gnu.org/software/easejs/manual/easejs.html#Imple...](https://www.gnu.org/software/easejs/manual/easejs.html#Implementation-
Details)

I also wrote a paper on some of the concepts:

[https://mikegerwitz.com/papers/coope/coope.pdf](https://mikegerwitz.com/papers/coope/coope.pdf)

The `class' keyword in JS still leaves much to be desired; it's just syntatic
sugar around the prototype model. There's nothing wrong with that model---it's
just important to understand how it differs from what OOP developers
traditionally expect.

~~~
nostalgeek
> it's just syntatic sugar around the prototype model.

No it allows "super" late binding, something you cannot do with functions and
prototypes. It's not just "sugar".

------
ahulab
another interesting way to understand JS classes is to look at the transpiled
Babel output of a class definition and extension

[like
this]([https://babeljs.io/repl/#?babili=false&browsers=&build=&buil...](https://babeljs.io/repl/#?babili=false&browsers=&build=&builtIns=false&code_lz=MYGwhgzhAEBiD29oG8BQBIY8B2EAuATgK7B7wEAUAlCqtPdOngBYCWEAdGNALzQDkYfnQYBfESPQBbAJ4AzItmDVaDRgQCmeIgWwDZCpcLXjxqUJBgAhMAWgaAHng3YAJjARJkooA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&lineWrap=false&presets=es2015%2Ces2016%2Ces2017%2Clatest%2Creact%2Cstage-0%2Cstage-1%2Cstage-2%2Cstage-3%2Ces2015-loose&prettier=false&targets=&version=6.26.0&envVersion=))

------
kyberias
This article didn't demonstrate any risks involved in using 'class' in
Javascript by devs familiar with other OOP languages. What exactly is the non-
common feature in Javascript OOP that exists in other OOP languages and
therefore poses such a risk that makes developes make "a lot of mistakes"?
It's not messing around with prototypes, since those devs don't understand
that any way.

Sounds like Javascript dev who is pissed because the language evolves and his
prototype-hack-knowledge becomes more and more obsolete.

------
chris_wot
What, that's all it does? Stop someone from accidentally not using the new
keyword?

I'm not dissing it, that's great it does this. But I'm a bit surprised (and
happy!) that this is all it does.

~~~
kowdermeister
It's not really about the new keyword. It adds a lot more and simplifies a lot
of things:

\- no more messing with .constructor, .prototype

\- calling parent object is super() easy :)

\- you can have static methods

\- no need to even type function for methods

[https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Classes)

~~~
hajile
You should never be using `.constructor` anyway because it isn't reliable
because a very common pattern replaces the `.prototype` with a new object
who's `.constructor` is `Object`

    
    
        var Foo = function () {};
        Foo.prototype = {
          bar() { return 'bar'; }
        };
        var f = new Foo();
        f.constructor === Object;
    

When you use `class` you are still messing with `.prototype`. The danger is
that programmers new to JS don't understand that they are and don't understand
why that matters.

Calling `super` is very anti-pattern in JS. Unlike classic OOP, parents are
not static. Lots of very common things can rip that rug out from under you by
modifying the parent in many different ways (createProperty, Symbols,
proxies/reflection, etc).

Static methods do indeed exist without classes.

    
    
        class Foo {
          static bar() { return 'bar'; }
        }
    
        //is identical to
    
        function Foo() {}
        Foo.bar = function() { return 'bar'; }
    

If you use `Object.create()` and ES6 object literal syntax you don't have to
type `function` either. You get the added bonus of being easily able to wrap
it in a normal function that avoids constructor weirdness

    
    
        var someProto = {
          bar() { return 'bar'; }
        }
        var myInst = Object.create(someProto);

~~~
geoelectric
To be absolutely fair, the pattern at the top is buggy in that you're supposed
to also set

    
    
        Foo.prototype.constructor = Foo;
    

...after replacing the prototype object that way. MDN (for example) covers
this in a few pages, including:

[https://developer.mozilla.org/en-
US/docs/Learn/JavaScript/Ob...](https://developer.mozilla.org/en-
US/docs/Learn/JavaScript/Objects/Inheritance)

That said, it's a pretty common bug, particularly with stuff like:

    
    
        Foo.prototype = Object.create(FooParent.prototype) // Crockford-style inheritance
        console.log(Foo.prototype.constructor.name) // "FooParent"

------
jdeisenberg
I do not know if anything has changed since this discussion:
[https://github.com/getify/You-Dont-Know-
JS/blob/master/this%...](https://github.com/getify/You-Dont-Know-
JS/blob/master/this%20%26%20object%20prototypes/apA.md) but it seems that
classes may not always be the answer.

~~~
stupidcar
A lot of these examples seem rather contrived, and the whole appendix has a
very FUDy feel to it. It seems like the author didn't like classes in JS, and
set-out to come up with some examples to justify their dislike, rather than
coming to that conclusion through actually encountering these problems in the
real world. Something not helped by the weaselly tone, constantly implying
that it's just raising concerns, when it's clearly been written by somebody
with an extremely strong anti-classes mindset.

As a simple way to define a reusable bundle of behaviour of logic, or to
construct simply base-class -> specialisation inheritance hierarchy, JS
classes work fine.

~~~
Can_Not
Totally agree, checkout this example:

> If you change/replace a method (on purpose or by accident) on the parent
> "class", the child "class" and/or instances will still be "affected"

I knew when reading this line exactly what the author was about to do to
mislead the unsuspecting user. Directly edit the prototype and feign surprise.
The prototype is representative of the class itself, basically a default of
all properties for that class. The correct (and obvious) way to override a
class method for only one member of the class is to assign a new function on
the property directly, not into the prototype's version of the property. Class
definitions being mutated in an application is extremely uncommon. This is
like putting your hand on a hot stovetop and then complaining that you burnt
yourself.

