ES6 In Depth: Classes

ES6 In Depth is a series on new features being added to the JavaScript programming language in the 6th Edition of the ECMAScript standard, ES6 for short.

Today, we get a bit of a respite from the complexity that we’ve seen in previous posts in this series. There are no new never-before-seen ways of writing code with Generators; no all-powerful Proxy objects which provide hooks into the inner algorithmic workings of the JavaScript language; no new data structures that obviate the need for roll-your-own solutions. Instead, we get to talk about syntactic and idiomatic cleanups for an old problem: object constructor creation in JavaScript.

The Problem

Say we want to create the most quintessential example of object-oriented design principles: the Circle class. Imagine we are writing a Circle for a simple Canvas library. Among other things, we might want to know how to do the following:

  • Draw a given Circle to a given Canvas.
  • Keep track of the total number of Circles ever made.
  • Keep track of the radius of a given Circle, and how to enforce invariants on its value.
  • Calculate the area of a given Circle.

Current JS idioms say that we should first create the constructor as a function, then add any properties we might want to the function itself, then replace the prototype property of that constructor with an object. This prototype object will contain all of the properties that instance objects created by our constructor should start with. For even a simple example, by the time you get it all typed out, this ends up being a lot of boilerplate:

<pre>
function Circle(radius) {
this.radius = radius;
Circle.circlesMade++;
}

Circle.draw = function draw(circle, canvas) { /* Canvas drawing code */ }

Object.defineProperty(Circle, "circlesMade", {
get: function() {
return !this._count ? 0 : this._count;
},

set: function(val) {
this._count = val;
}
});

Circle.prototype = {
area: function area() {
return Math.pow(this.radius, 2) * Math.PI;
}
};

Object.defineProperty(Circle.prototype, "radius", {
get: function() {
return this._radius;
},

set: function(radius) {
if (!Number.isInteger(radius))
throw new Error("Circle radius must be an integer.");
this._radius = radius;
}
});
</pre>

Not only is the code cumbersome, it’s also far from intuitive. It requires having a non-trivial understanding of the way functions work, and how various installed properties make their way onto created instance objects. If this approach seems complicated, don’t worry. The whole point of this post is to show off a much simpler way of writing code that does all of this.

Method Definition Syntax

In a first attempt to clean this up, ES6 offered a new syntax for adding special properties to an object. While it was easy to add the area method to Circle.prototype above, it felt much heavier to add the getter/setter pair for radius. As JS moved towards a more object-oriented approach, people became interested in designing cleaner ways to add accessors to objects. We needed a new way of adding “methods” to an object exactly as if they had been added with obj.prop = method, without the weight of Object.defineProperty. People wanted to be able to do the following things easily:

  1. Add normal function properties to an object.
  2. Add generator function properties to an object.
  3. Add normal accessor function properties to an object.
  4. Add any of the above as if you had done it with [] syntax on the finished object. We’ll call these Computed property names.

Some of these things couldn’t be done before. For example, there is no way to define a getter or setter with assignments to obj.prop. Accordingly, new syntax had to be added. You can now write code that looks like this:

<pre>
var obj = {
// Methods are now added without a function keyword, using the name of the
// property as the name of the function.
method(args) { ... },

// To make a method that's a generator instead, just add a '*', as normal.
*genMethod(args) { ... },

// Accessors can now go inline, with the help of |get| and |set|. You can
// just define the functions inline. No generators, though.

// Note that a getter installed this way must have no arguments
get propName() { ... },

// Note that a setter installed this way must have exactly one argument
set propName(arg) { ... },

// To handle case (4) above, [] syntax is now allowed anywhere a name would
// have gone! This can use symbols, call functions, concatenate strings, or
// any other expression that evaluates to a property id. Though I've shown
// it here as a method, this syntax also works for accessors or generators.
[functionThatReturnsPropertyName()] (args) { ... }
};
</pre>

Using this new syntax, we can now rewrite our snippet above:

<pre>
function Circle(radius) {
this.radius = radius;
Circle.circlesMade++;
}

Circle.draw = function draw(circle, canvas) { /* Canvas drawing code */ }

Object.defineProperty(Circle, "circlesMade", {
get: function() {
return !this._count ? 0 : this._count;
},

set: function(val) {
this._count = val;
}
});

Circle.prototype = {
area() {
return Math.pow(this.radius, 2) * Math.PI;
},

get radius() {
return this._radius;
},
set radius(radius) {
if (!Number.isInteger(radius))
throw new Error("Circle radius must be an integer.");
this._radius = radius;
}
};
</pre>

Pedantically, this code isn’t exactly identical to the snippet above. Method definitions in object literals are installed as configurable and enumerable, while the accessors installed in the first snippet will be non-configurable and non-enumerable. In practice, this is rarely noticed, and I decided to elide enumerability and configurability above for brevity.

Still, it’s getting better, right? Unfortunately, even armed with this new method definition syntax, there’s not much we can do for the definition of Circle, as we have yet to define the function. There’s no way to get properties onto a function as you’re defining it.

Class Definition Syntax

Though this was better, it still didn’t satisfy people who wanted a cleaner solution to object-oriented design in JavaScript. Other languages have a construct for handling object-oriented design, they argued, and that construct is called a class.

Fair enough. Let’s add classes, then.

We want a system that will allow us to add methods to a named constructor, and add methods to its .prototype as well, so that they will appear on constructed instances of the class. Since we have our fancy new method definition syntax, we should definitely use it. Then, we only need a way to differentiate between what is generalized over all instances of the class, and what functions are specific to a given instance. In C++ or Java, the keyword for that is static. Seems as good as any. Let’s use it.

Now it would be useful to have a way to designate one of the methods of the bunch to be the function that gets called as the constructor. In C++ or Java, that would be named the same as the class, with no return type. Since JS doesn’t have return types, and we need a .constructor property anyway, for backwards compatibility, let’s call that method constructor.

Putting it together, we can rewrite our Circle class as it was always meant to be:

<pre>
class Circle {
constructor(radius) {
this.radius = radius;
Circle.circlesMade++;
};

static draw(circle, canvas) {
// Canvas drawing code
};

static get circlesMade() {
return !this._count ? 0 : this._count;
};
static set circlesMade(val) {
this._count = val;
};

area() {
return Math.pow(this.radius, 2) * Math.PI;
};

get radius() {
return this._radius;
};
set radius(radius) {
if (!Number.isInteger(radius))
throw new Error("Circle radius must be an integer.");
this._radius = radius;
};
}
</pre>

Wow! Not only can we group everything related to a Circle together, but everything looks so… clean. This is definitely better than what we started with.

Even so, some of you are likely to have questions or to find edge cases. I’ll try to anticipate and address some of these below:

  • What’s with the semicolons? – In an attempt to “make things look more like traditional classes,” we decided to go with a more traditional separator. Don’t like it? It’s optional. No delimiter is required.

  • What if I don’t want a constructor, but still want to put methods on created objects? – That’s fine. The constructor method is totally optional. If you don’t supply one, the default is as if you had typed constructor() {}.

  • Can constructor be a generator? – Nope! Adding a constructor that’s not a normal method will result in a TypeError. This includes both generators and accessors.

  • Can I define constructor with a computed property name? – Unfortunately not. That would be really hard to detect, so we don’t try. If you define a method with a computed property name that ends up being named constructor, you will still get a method named constructor, it just won’t be the class’s constructor function.

  • What if I change the value of Circle? Will that cause new Circle to misbehave? – Nope! Much like function expressions, classes get an internal binding of their given name. This binding cannot be changed by external forces, so no matter what you set the Circle variable to in the enclosing scope, Circle.circlesMade++ in the constructor will function as expected.

  • OK, but I could pass an object literal directly as a function argument. This new class thing looks like it won’t work anymore. – Luckily, ES6 also adds class expressions! They can be either named or unnamed, and will behave exactly the same way as described above, except they won’t create a variable in the scope in which you declare them.

  • What about those shenanigans above with enumerability and so on? – People wanted to make it so that you could install methods on objects, but that when you enumerated the object’s properties, you only got the added data properties of the object. Makes sense. Because of this, installed methods in classes are configurable, but not enumerable.

  • Hey, wait… what..? Where are my instance variables? What about static constants? – You caught me. They currently don’t exist in class definitions in ES6. Good news, though! Along with others involved in the spec process, I am a strong proponent of both static and const values being installable in class syntax. In fact, it’s already come up in spec meetings! I think we can look forward to more discussion of this in the future.

  • OK, even still, these are awesome! Can I use them yet? – Not exactly. There are polyfill options (especially Babel) so that you can play around with them today. Unfortunately, it’s going to be a little while before they are natively implemented in all major browsers. I’ve implemented everything we discussed here today in the Nightly version of Firefox, and it’s implemented but not enabled by default in Edge and Chrome. Unfortunately, it looks like there’s no current implementation in Safari.

  • Java and C++ have subclassing and a super keyword, but there’s nothing mentioned here. Does JS have that? – It does! However, that’s a whole other post’s worth of discussion. Check back with us later for an update about subclassing, where we’ll discuss more about the power of JavaScript classes.

I would not have been able to implement classes without the guidance and enormous code review responsiblity of Jason Orendorff and Jeff Walden.

Next week, Jason Orendorff returns from a week’s vacation and takes up the subject of let and const.

About Eric Faust

More articles by Eric Faust…


40 comments

  1. Cris Mooney

    Excellent job targeting the experienced JavaScript programmer audience, rather than just those coming from languages with classes already exist and assumptions these abilities are all 100% new to JavaScript. The transpilers can be helpful helpful in this respect (if anyone looks at output like at https://babeljs.io/repl), but this is the first I seen of someone take the time to acknowledge and write out how class related functionality can already be done, and then so cleanly guide into the new syntax support and why even this much of the syntax is valuable (and relates to traditional syntax), despite not adding “new functionality” — all this value even before getting to “extends”. A great logical presentation, well written, with a smart predictive Q&A, that should encourage adoption of the new features as well as respect for the language that already exists. Thank you!

    July 23rd, 2015 at 07:46

  2. Jovica Aleksic

    I find it noteworthy that instance variables can very well be implemented when using babel. I understand that the series is about es6 features, and property initializers are only an early es7 proposal. However, syntactically, they do work with es6 classes just eine, allowing you to write very concise code. And i guess a majority of es6 developers use babel anyways.
    So, after configuring babel to use stage=0, you can write like this:

    class Foo {
    bar = ‘some value’;
    static bar = ‘some static value’;
    }

    The result is the same as this:

    class Foo {
    constructor() {
    this.bar = ‘some value’;
    }
    }
    Foo.bar = ‘some static value’;

    July 23rd, 2015 at 10:45

    1. Eric Faust

      Thanks for the tip about Babel! That’s useful. And in fact, that’s exactly the syntax I favor personally, with the addition of |const| as another modifier for the instance variables.

      You can, of course, add instance variables in the constructor, but for static variables, adding them afterwards is really unsavory. This was exactly the thing we just fought to get rid of!

      From my point of view, the dream is to write something were you can say:

      class Life {
      static const ANSWER = 42;
      }

      and have that be installed non-writable non-configurable (at least!) on the Life constructor. We’ll see where we get with the es7 proposal.

      July 24th, 2015 at 11:26

  3. Phil Ricketts

    This looks awesome.

    I’ve been thinking about the role of ES6 and as someone who writes a lot of javascript for the browser in distributed project teams, I’ve decided to just focus on writing good ES5 for the following reasons:
    * ES6 support in browsers has performance issues (because it hasn’t had all of the lovely optimisation that ES5 has over time)
    * Transpiling is an extra authoring step (+ browsers not really ready)
    * Transpiling may introduce strange effects, as code is run in the browser that has been interpreted through the transpiler
    * Supporting multiple implementations of ES6 in the browser would introduce CSS-like support differences which adds inevitable overhead due to varied support
    * Developer ergonomics not most important to me – writing good ES5 is

    July 23rd, 2015 at 11:12

    1. Miguel Camba

      That is not accurate. Point by point:

      “ES6 support in browsers has performance issues”. While this is true, should not concern you because A) this is very unlikely to ever be your bottleneck B) You are going to transpile code to ES5, so even if ES6 was slow as hell it won’t affect you.

      “Transpiling is an extra authoring step (+ browsers not really ready)” Indeed is an extra authoring step. Not cool, but a necessary evil for now, but if you are transpiling why do you mention browser support?

      “Transpiling may introduce strange effects, as code is run in the browser that has been interpreted through the transpiler” Although I think it is possible to load the transpiler in the browser and do the transpilation in the client side, I think nobody does that. People transpiles code in “compilation time”, so the browser never receives ES6. And the transpilation is very rock solid. Sometimes can be a bit less readable that hand crafted JS, but nothing else.

      “Supporting multiple implementations of ES6 in the browser would introduce CSS-like support differences” Again, since you are transpiling this is not a concern. No ES6 specific differences, just the ones we all know and love.

      “Developer ergonomics not most important to me – writing good ES5 is” I personally disagree, but this is subjective, so nothing to argue here.

      July 28th, 2015 at 04:02

  4. bogus

    How do you mixin into an ES6 class?

    July 23rd, 2015 at 13:03

    1. Eric Faust

      I saw some rumblings about a |mixin| keyword proposal a while back, but I lost track of what was happening with it.

      There’s no reason that a normal extend() based approach won’t work for classes, it’s just not immediately built in to the syntax.

      From the point of view of coming up with a syntax that covers everything in this problem space, this is a deeply unsavory answer, but at least it’s a little better than it was.

      July 24th, 2015 at 11:29

      1. Christian Martin

        Well you can also use decorators as mixin.

        July 24th, 2015 at 12:10

    2. Jason Orendorff

      One way to use a mixin is:
      class MyClass extends mix(MixinClass1, MixinClass2, ...) { ... }

      But the standard does not define this mix() function; you’d have to write it yourself. I think it would mainly need to create a prototype object that combines all the features of all the mixins, and then make a simple constructor and attach the prototype to it. This is not so bad: a few lines of code, using Object.assign().

      In other words, you basically build your own mixin support in JS, exactly like you would with ES1-5. That is possible because ES6 classes use exactly the same simple, hackable underlying model. It just looks prettier when you’re done!

      July 30th, 2015 at 08:18

  5. Eric Elliott

    I love the class syntax, but the semantics are really broken in ES6.

    Using `class` forces you to use `new` from all the call sites, which makes it harder to switch implementations to factories later (a common enough refactor that it’s in Martin Fowler’s seminal [refactoring catalog](http://refactoring.com/).

    As for inheritance and super (which you will address soon, obviously), [those are bad ideas](https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3) that have no place in JavaScript to begin with, in my opinion.

    Much better are the decorators and traits being talked about for ES7. As the famous Gang of Four says, “Favor object composition over class inheritance.”

    Or [just use stamps](https://github.com/stampit-org/stampit). The stamp community has been very active lately, and we’ve been working on an [open specification for stamps](https://github.com/stampit-org/stamp-specification). It would be really great to see support added to native JavaScript.

    July 23rd, 2015 at 15:59

    1. Jeff Walden

      You’ve been broken a little bit by languages like Java. :-) In such languages, constructors can’t be factory methods — they have to return whatever this was created by them.

      In contrast, in JS, a function called using new can return any object whatsoever, not just the default this.

      class X { constructor() { return new String(‘ohai’); } };
      assert(new X() instanceof String);

      Requiring use of new at call sites in no way prevents you from making factories out of them.

      I strongly agree that inheritance is very often a bad idea. Particularly so when inheriting from most of the basic functionality in JS and the DOM. But the near-ubiquity of inheritance in so many of the various class-like implementations in ES5 suggests adding inheritance was worth it even if it’d be abused. I have in the back of my mind the idea of writing a post discouraging use of inheritance, or at least counseling caution about overusing it, but I’m not sure when I’ll get around to writing it.

      July 24th, 2015 at 16:53

      1. Eric Elliott

        I wish it were so simple, but it’s not.

        Returning anything other than an instance of the constructor is a breaking change to API callers. Fine if all the code is in your castle, but if it’s a library/public API, you’re SOL. Issues are discussed in more depth here: https://medium.com/javascript-scene/how-to-fix-the-es6-class-keyword-2d42bb3f4caf

        July 24th, 2015 at 18:55

        1. Eric Elliott

          I cleared up some more misconceptions about inheritance in JavaScript in this post: https://medium.com/javascript-scene/common-misconceptions-about-inheritance-in-javascript-d5d9bab29b0a

          Check out the sections on `new` and `instanceof`.

          July 24th, 2015 at 19:06

        2. Jeff Walden

          If people depend on return value type, it’s irrelevant that it comes from new or from a classical factory method. The canonical Java Point pool example, returning cached instances from a Point.create(int x, int y) method, can as easily be done in JavaScript for new Point(x, y).

          function Point(x, y) { if (!Point._cache) Point._cache = {}; var cache = Point._cache; if (!(x in cache)) cache[x] = {}; if (y in cache[x]) return cache[x][y]; this.x = x; this.y = y; cache[x][y] = this; }

          Conversion to class syntax left as an exercise for the reader.

          It’s up to you whether a function being constructed returns only instances of itself or not. So long as it does only return instances of itself, there’s no “breaking change to API callers”.

          July 25th, 2015 at 16:03

  6. Dmitry Soshnikov

    Good write up!

    > What’s with the semicolons? – In an attempt to “make things look more like traditional classes,”

    I think we need to make a consistent style across the glob in this case, and don’t introduce several styles based on “traditional classes” (traditional from where, BTW?) ES6 spec says nothing about semicolons there, so you just put useless empty statement after each method definition.

    July 24th, 2015 at 12:10

    1. Eric Faust

      > ES6 spec says nothing about semicolons there, so you just put useless empty statement after each method definition.

      This is not quite true. The spec very explicitly allows semicolons in class definitions and very explicitly disallows arbitrary statements.

      See http://www.ecma-international.org/ecma-262/6.0/#sec-class-definitions for the full grammar. The last line of the ClassElement production is the one of interest.

      It is true that the semicolon there does not evaluate to anything, so in that sense you are correct. As noted in the post, the semicolons are not necessary. I added them almost by muscle memory, because I come from a C++ background.

      July 24th, 2015 at 12:26

  7. Dmitry Soshnikov

    > The spec very explicitly allows semicolons

    Oh, interesting, thanks! Yeah, my bad, I don’t remember this change (probably a recent one). OK, then, yeah, I predict we’ll have some inconsistent styles in the future :) Or will nevertheless come up with a single one (with or without semicolons), and it will be job of a linter to maintain one style.

    July 24th, 2015 at 16:03

  8. Dmitry Soshnikov

    Also, it would be truly better, had they been separated by commas, as in Object Literals. Now we have huge inconsistency: in Object Literals methods are separated (as any other property) by a comma, while in a class: either with no anything, or with a semicolon :) Alas, ES6 spec is shipped already.

    July 24th, 2015 at 16:15

    1. Allen Wirfs-Brock

      We considered all of these possibilities, including the possibility of allow semi-colon to be used as a separator within object literals.

      Comma is too limiting for possible future extensions. For example, if something like let/const (or private/protected) declarations were added to class bodies we would want to be able to have comma separated identifier lists in them. So, we settled on using statement-like semi-colon separators in class bodies.

      I favored also allowing the use of semi-colons as a separator within object literals, but we weren’t able to achieve consensus on that within TC39. Maybe in the future…

      July 24th, 2015 at 17:32

    2. Simon Speich

      I also would have expected/preferred it to be commas…

      July 27th, 2015 at 02:25

  9. PhistucK

    It is already implemented in Chrome and enabled by default. It just requires strict mode, so try this in the Developer Tools (I tried Chrome 43) –
    (function () { “use strict”; class a {} }())
    It does not throw a syntax error.
    Edge enabled it by default as well, as far as I know. Perhaps it also requires strict mode, though.

    July 25th, 2015 at 11:10

  10. Neil Stansbury

    This is unfortunately yet more Java-isation of JS, mostly from people who seem to struggle with the premise of prototypes, and everythings-an-object.

    All I ever felt was lacking was static prototypical inheritance declarations – a formalisation of __proto__ that accepted an array of objects to composite from.

    “`
    RedCircle.prototype = {
    __proto__ : [Shape, Compass, Crayon]
    }
    “`

    July 27th, 2015 at 03:45

    1. Jason Orendorff

      > This is unfortunately yet more Java-isation of JS

      “yet more” in addition to what?

      To me, ES6 classes aren’t Java-like at all. The underlying constructor and prototype created by an ES6 class declaration are exactly the same ones you would create by hand in ES1-5.

      How does concise, declarative syntax for a good idea turn it into a bad idea?

      > mostly from people who seem to struggle with the premise of prototypes, and everythings-an-object.

      I wonder which people you have in mind. ES6 classes were designed and championed by Allen Wirfs-Brock, whose OO bona fides are as legit as they come. He worked on Smalltalk. Other TC39 members (who debated many exotic class designs before settling on this simple one) are heavy hitters too. Mark Miller led the team behind the E programming language. Tom van Cutsem designed proxies (so I suppose he knows what prototypes are). Brendan Eich put prototypal inheritance into JS to begin with.

      These people are not infallible, but it’s silly to claim they “struggle with the premise” of JS.

      A lot of objections to ES6 classes seem to amount to “but won’t other programmers use this in ways I don’t like?” Or, “won’t other programmers use this as an excuse to avoid learning things I care about?” If experience is any guide, you can relax. The effect will be small, and there is no danger of Java-style API design becoming the preferred practice in JS.

      July 30th, 2015 at 09:17

      1. Eric Elliott

        “The underlying constructor and prototype created by an ES6 class declaration are exactly the same ones you would create by hand in ES1-5.”

        The only way it’s remotely similar is if you’re trying to simulate classical inheritance in JavaScript. That in itself is a bad idea.

        https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3

        July 30th, 2015 at 10:47

  11. Dominik

    In the ES5 example, when setting a new prototype object it should restore the constructor property:

    Circle.prototype = {
    area: function area() {
    return Math.pow(this.radius, 2) * Math.PI;
    },
    constructor: Circle
    };

    This usually matches ES6 class behavior as a class’s constructor() method will establish that kind of relationship automatically. However ES6 seems to be stricter as the generated Circle.prototype.constructor property is non-writeable, non-enumerable, and non-configurable.

    July 27th, 2015 at 06:48

  12. simonleung

    ES6 class is not an advance of ES5, it is an alternative of ES5 contructor function and specific on creating object.

    For a class C,
    C also has prototype property
    C.prototype.constrcutor===C;

    But,
    C cannot be “re-declared”, otherwise, TypeError triggered.
    C.prototype cannot be “replaced” by a new object.
    cannot call C directly, ie. C();
    “return …” in constructor() of C will be ignored.

    July 28th, 2015 at 09:52

    1. Jeff Walden

      I don’t think your last point is correct. A class constructor can return a different value from the this it would ordinarily return. This works perfectly well in SpiderMonkey, e.g. new (class C { constructor() { return new String("hi"); } })() is a String object. And by my reading of ES6, 14.5.14 step 12 creates the constructor using the common DefineMethod operation in 14.3.8 used to define all other methods, and those methods’ return values certainly aren’t ignored. Or am I missing something?

      July 28th, 2015 at 10:04

      1. simonleung

        Well,my bad.
        It would be ignored only when constructor returning a non-object value per spec. 6.1.7.3
        This apply to Function constructor as well.

        July 28th, 2015 at 11:12

  13. Cris Mooney

    After contemplating the “class” syntax alternative, a better organization of code for some [mostly coming from elsewhere], the following comment from this article stands out:

    > There’s no way to get properties [and prototype] onto a function as you’re defining it.

    QUESTION: Since there seem to be a number of folks here that were involved in the new specification, is someone able to elaborate why the syntax of “function” could not have been enhanced to support this rather than introduce an effectively competing syntax?

    While there is value to welcoming those from other environments by offering familiarity, there are also damaging aspects of this that can discourage understanding of language fundamentals when one hides core current language functionality by encouraging alternative competing syntax.

    I feel I have already seen new entrants coming into Javascript from other languages embrace the new “class” syntax violently at the expense of understanding what already exists. Fostering that mindset trades one incomplete solution for another, as novice entrants are most often want to do, instead of moving to merge the best aspects of both towards a better solution.

    I hope someone can explain why a creative compatible solution to the critical key objection “there’s no way to get properties onto a function as you’re defining it” could not be found, so that we could maintain the language and reveal its power to new users, while providing the ability to organize code in the ways that new entrants can recognize, and with very close to equivalent brevity. Was it just cow-towing, laziness, lack of vision, or is there some real an insurmountable reason?

    July 29th, 2015 at 07:53

    1. Cris Mooney

      Tangent: I’d like to clarify cow-towing, laziness, lack of vision: I suffer from each of these all the time as much as any productive person. They can be 100% justifiable for anyone who works hard, and I do not hold anyone in disregard unless they are chronically over used. Anyone who works hard, and those specifying Javascript are certainly smart and hard working, is likely to not to get everything “right” (and even “right” is subjective in many cases). Those responses are acceptable, and respectable, and I mean them as such. Those who can’t accept them as such do so out of unreasonable pride and insecurity, which others should not be held responsible for. A true scientist embraces failure as much as success, since it improves clarity. I just want to understand “why”, so I can better appreciate and be productive.

      July 29th, 2015 at 08:43

      1. Jason Orendorff

        Well, I think the specific piece of justification you’ve seized on is not a particularly important one.

        To me, class syntax has two major benefits:

        1. The ES1-5 way of creating classes is procedural and wordy. Declarative code is good; brevity is good.

        I’ve put a whole ES6 class on a presentation slide when the corresponding ES5 would have been too much. (It was this talk: https://github.com/jorendorff/game-playing/blob/master/talk.md )

        Of course we do not usually write code to fit on slides. But what is good for one code-reading audience is good for others too.

        2. There’s a uniformity and correctness benefit: it’s easier to get the details right using the new class syntax. Especially when subclassing.

        I like that methods are non-enumerable. I like that “static” methods are really “class methods” (in the Smalltalk sense) and are inherited by subclasses. I like that .prototype and .constructor are automatically hooked up.

        This feature was not added in order to “provide familiarity”. It’s about paving existing cowpaths. The cows did not have to be towed; they’ve been treading this trail for decades already.

        July 29th, 2015 at 15:50

        1. Neil Stansbury

          Jason I think that’s the danger – forcing square pegs into round holes, and applying traditional OO paradigms to JS where they don’t marry.

          As I’m sure you are aware, there is no such thing as “classes” in ES1-5 either literally or figuratively and even the ES6 class declaration is in reality little more than syntactic sugar.

          The “class” declaration doesn’t give provide some notion of an immutable prototype chain, and any one who thinks that their JS “classes” are like Java or C++ is in for a shock.

          The only real “pain” in ES prior to 6 was that the constructor was declared separately to the prototype, and that “extends” provides a declarative formalisation of __proto__ to fulfill the prototype chain.

          Aside from that, an ES6 class under the hood delivers nothing more than brevity. ES5 can already do everything the class declaration delivers, so I’d suggest that this is exactly about “providing familiarity” to non-ES users because this cow path was paved some time ago in ES.

          Worst of all, it hides the core prototypical concepts from non-ES users that JS is hoisted on and provides a set of expectations that either aren’t true or at best are an approximation, I saw this first hand recently working with a team of ex-AS3 devs migrating to TypeScript who were lost in the traditional “inheritance” vs prototypical “delegation” concepts.

          I’m sure ES6 will be a great success – but then ES5 already was, and personally I’m sad to see “old fashioned” OO concepts polluting the premise of composition & delegation over inheritance, and I worry that this is more than just another JS pattern in its already formidable arsenal. I suspect over the next few years we will see an awful lot of “Java-script”.

          I thought this post was quite interesting: http://christianalfoni.github.io/javascript/2015/01/01/think-twice-about-classes.html

          July 29th, 2015 at 20:15

          1. Jason Orendorff

            Cutting to the chase:

            > Aside from that, an ES6 class under the hood delivers nothing more than brevity.

            I’ve already said what I have to say about this (“Declarative code is good; brevity is good”, “There’s a uniformity and correctness benefit”, “paving existing cowpaths”).

            The complaint that ES6 classes “hide” the underlying model is kind of weird to me. It’s not like it’s a big secret.

            All declarative syntax and APIs hide some procedural stuff going on behind the scenes… and declarative syntax and APIs are still better. Preventing complexity from appearing repeatedly in your code is a good thing.

            If we were adding object literals today, people could logically have all the same complaints. “This hides all the Object.defineProperty() calls that are happening behind the scenes! Now no one will understand property attributes! No one will understand that an object can have whatever prototype you want! This was all possible with property assignment already. Look at my three-line toy function that builds something like an object literal from an array—so much better! This will force everyone into a mindset of using own properties for everything.”

            But object literals are actually really nice.

            > I thought this post was quite interesting: …

            A couple comments about Christian’s post. Both of these should come as good news, I hope!

            One, Christian seems to be thinking of class as an exclusive alternative to everything else, and not as a building block that can be used in combination with the tricks he already knows. Follow-up post opportunity!

            Two, classes aren’t “locking developers in” to a single model of programming. If you’ve used basically any recent language other than Java and Ruby, you know that languages that do not require everything to be a class do not in fact have an everything-has-to-be-a-class culture. C++ doesn’t. Python doesn’t. F# doesn’t. JS won’t either.

            ES6 classes are a handy tool. I enjoy using them. Like all good things, they are best used in moderation.

            July 30th, 2015 at 10:28

          2. Cris Mooney

            I’d have done well to look up Kowtow spelling. But, I appreciate the creative follow up on my blunder, and so I’ll embrace my screw up.

            We’ve a “class sledgehammer” problem here which is the momentum that comes with term “class” and natural human loyalty, tossed into an arena that already has an interesting object paradigm. While Jason may claim the best will get used, that is simply not true. Sure, he may be exceptional and rise above humanity. But unique folks can’t speak for people in general.

            In truth we’ll see a massive wave of programmers exclusively using the new high profile “class” out of familiarity, without consideration of the option they don’t comprehend. Easy is what most people do. And, popularity is not always smart. Javascript has been successful substantially because of what it is, and the fact is that can be diluted and lost.

            Here is where the “kowtow” comes into play: I am not currently in support of simply converting from square to round holes – I like the round holes too, and claim many who haven’t used those are also in for a shock. When a language change is made there should more compelling reasons than “that is how my cow path does it.” Instead, keep the good parts of the original path, and add in the good parts of the another as well – compatibly. In my decades programming, I’ve seen too much simply replacing X with Y instead of working to a new Z. The term “class” has too much weight to do any other than replace, at least in the short term.

            We all seem to agree organization with objects and forms of inheritance can be useful, and many of do find the “new “, “.prototype” and “defineProperty” syntax weak in this regard.

            However, “class” is not the only alternative way to organize/communicate. We have Object.create() and functions returning literals, which can be also be clear, terse, and arguably more progressive. I think these could have been refined with less impact, improving the language while prompting appreciation of its unique value. Like the enhancement to support defineProperty functionality inline, a few more minor changes could add desired enhancements, improve clarity and simplicity, without encouraging an extreme “class” wave.

            Further on these enhancements, while I do not follow the gospel that an immutable inherited protype chain is paramount, I do support it as a useful feature desirably made easily accessible, for cases where applicable, and to support those who swoon over it. Just not as default with top billing, promoting over-use.

            So, while I get it that changes are here to stay, I’m still not clear on exactly why this path was taken. I remain tentative and less likely to use it, and yet I also remain curious about exactly what I might be missing that prevented a more progressive path of moderation.

            To me it still just seems classy folks won’t be happy until it all looks exactly like what they are familiar with. Yes, changes are desirable to tighten it up and add more power, but they need not stray so far.

            I’ve only thought about the “formatting/brevity” aspect possibilities a bit, and I’m sure another could do better, but consider/critique my preliminary pondering at https://jsfiddle.net/0tw153g3/. Some will find my example/test obvious and trivial, other may have ideas triggered. Either way, a decent start as others have done, and I think a few more language tweaks here and there gets you far enough, without trashing often under appreciated value in JavaScript. Perhaps this is what Erik Elliott has refined better in his https://github.com/stampit-org/stampit, but I wanted to see where I came out before being bias by any more specific implementations.

            I’d finally remind that the syntax/structure of actual definitions, class syntax or otherwise, should be a small part of any serious enough program requiring significant organization. The logical instructions (dare I mutter procedural) about what we want the computer to actually do remain the bulk of the purpose for those who are heavily invested in a language. So, brevity and simplicity in this specific area seems a bit over blown to me, not requiring the intro of such a controversial, heavily loaded, high profile, term.

            At least that is where I remain at this point until I am enlightened by some less “I just love classes” reasons. I still see no reason not to have added the things one likes about classes, rather than breaking out a sledge hammer.

            Thanks for the talk! I promise to finally shut up now.

            July 30th, 2015 at 12:48

  14. PhistucK

    @Cris Mooney –
    I think you are little too late into the game, as the specification has been finalized already… :( You had about two years to post your feedback (did you?) to the relevant standards body…

    Anyway, the configurability issue (I assume you are talking about that, like, {configurable: false, enumerable: true} or similar) could be solved with ECMAScript 7 (2016?) decorators, in case they are accepted –
    https://github.com/wycats/javascript-decorators
    It can probably resolve other similar issues as well.

    July 29th, 2015 at 08:18

  15. Cris Mooney

    @PhistucK, I am not here to change the spec, only asking for enlightenment as to what I am missing. I know lots of very smart folks are involved, and do not believe they missed out on having another cook like me in the kitchen.

    I honestly want to know if my question is valid, and hopefully hear something that will help me, and perhaps others, appreciate why the choices were made so that we can come even more fully on board.

    @Eric Elliott and others make good points, and I know others like @Allen Wirfs-Brock have indicated they have deep inside knowledge as well. I’d like to not hear “too late”, but since this seems an interactive and educational forum I look to hear them help me appreciate what I have not yet considered.

    Tangent: you can only assume that I am talking about “there’s no way to get properties onto a function as you’re defining it” and the apparent solution (in this educational web page’s investigation) of “class”, since that I what I wrote about.

    July 29th, 2015 at 08:30

  16. simonleung

    It is pointless to introduce class sytnax if you dont mention class inheritance, which is missing in ES5 constructor function.

    July 29th, 2015 at 09:58

  17. yunpeng

    FYI:in the article, dfferentiate =》differentiate, : -)

    August 6th, 2015 at 20:39

    1. Havi Hoffman [Editor]

      @yunpeng Thanks, well spotted. And fixed. :-)

      August 7th, 2015 at 11:37

  18. markg

    Very useful writeup, thanks. In the code samples can you make the line lengths shorter so you don’t have to scroll back and forth to read the comments? The column is narrow.

    August 8th, 2015 at 14:07

Comments are closed for this article.