I would never argue that ES6 classes are “clearly a better choice than composition, modules, or prototypal OO”. They are not. I’m a fan of purely prototypal inheritance (I’ve even written a library for it), I’d love it if the semantics of classes were based on it. But that didn’t happen, for the legitimate reason of backward compatibility.
ES6 classes have a few benefits:
- They are a standard for single inheritance. There are way too many inheritance libraries and we are already seeing some of them being replaced with ES6 classes (in Ext JS, in AngularJS 2, soon in Ember.js, to some degree in React).
- Related to the previous item: they help tools (such as IDEs and type checkers) analyze source code statically.
- You can subclass built-in constructors such as Error.
- They are compatible with current code that uses constructors.
In order to do OOP well, classes need to be complemented with traits (or mixins). I don’t like to call ES6 classes “classical inheritance”, because they are not really classes, just syntactic sugar for constructors (as you know).
We had years for developers to converge on a single approach for doing single inheritance. Unfortunately, that didn’t happen. Now we have something that is good enough and that currently is the best bet w.r.t. broad mainstream adoption.
Therefore, the burden of proof is on the alternatives: in what way are they significantly better than ES6 classes? Traits do provide something that ES6 classes don’t have, but they complement them, they are not an alternative to them. Other than that, if you can get yourself to ignore the mismatch between syntax and semantics, ES6 classes will work just fine.
ES6 classes are not the most elegant choice, but the most pragmatic one.