How to fix the `class` keyword

Dmitry Soshnikov dmitry.soshnikov at gmail.com
Wed Mar 4 17:33:27 UTC 2015


There is a trap one could fall into, after reading one "classic" or even
"authoritive" book. After that it might seem one understood the essence of
everything.

For fairness, there were couple of valid points noticed:
composition/aggregation (will be achieved, and already now can be, by
mixins), and, probably, that you'll have to refactor call sites if you
decide to switch to the raw factory (that's why eg Python chose to
instantiate without 'new' keyword).

Other than that, yes, factory is just a desugarred class pattern, and yes,
sugar matters if you want to design a practical language. In case of
class-based code reuse, prototypes were also desugarred version of it
(that's why JS at some point was "unexplained", and as a result made it
harder to be practical quicker). Eg in my class I give prototypes as an
advanced topic now, since it's just an implementation detail (the same
again as in Python, which is also delegation-based).

The most valuable advice would be from Eric Arvidsson - just to design your
language (likely, to be a "compiler" writer is easier nowadays), and
exactly there you'll see how sugar matters.

Dmitry

On Wednesday, March 4, 2015, Eric Elliott <eric at paralleldrive.com> wrote:

>
> I've already posted this on my Medium blog here:
> https://medium.com/@_ericelliott/how-to-fix-the-es6-class-keyword-2d42bb3f4caf
>
> It seems inevitable that the `*class*` keyword in JavaScript is going to
> catch on, but that’s a problem because it’s fundamentally broken in many
> ways.
>
> Now that it’s out there and people are using it, it seems like the only
> logical way forward is to *try to make it better for everybody*.
>
> In JavaScript, *any function can instantiate and return objects.* When
> you do so without a constructor, it’s called a *factory function*. The
> new `*class*` syntax *can’t compete with the power and flexibility of
> factories* — specifically stamps, and object pools are not the only
> factory use-case.
>
> There is a whole section on object construction in the GoF “Design
> Patterns” book
> <http://www.amazon.com/gp/product/0201633612?ie=UTF8&camp=213733&creative=393185&creativeASIN=0201633612&linkCode=shr&tag=eejs-20&linkId=XXUP5DXMFH5VS2UI> which
> exist only to get around the limitations of constructors and classes.
>
> See also: Three Different Kinds of Prototypal OO.
> <http://ericleads.com/2013/02/fluent-javascript-three-different-kinds-of-prototypal-oo/>
>
> The bottom line: *Class doesn’t give you any power* that isn’t already
> supplied by *factory functions* and the *prototypal OO* built into the
> language. All you’re doing when you create a class is opting into a *less
> powerfull, less flexible mechanism* and *a whole lot of pitfalls and
> pain.*
> <https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3>
>
> Is there any hope that the `*class*` keyword will ever be useful? *Maybe.*
> Why should we bother?
>
>
> Why don’t we just create a lint rule and move on?
>
> *The `class` keyword is creating a rift* in the JavaScript community. I
> for one have plans to create an ESLint config that prohibits `*class*`
> and share it as far and as wide as I can — and since I’m building a
> community of JavaScript developers that currently includes ~40,000 people,
> that’s far enough to have an impact.
>
> *Classes could be useful*. What if we want to build abstractions on top
> of it? What if we want to do more things in the language itself that could
> benefit with `*class*` integration (such as built-in *traits*)?
>
> *We could make these changes opt-in* by adding config to the class
> itself. That would prevent breaking changes and hopefully make the whole
> community happy. As it stands, we're just making people with classical
> inheritance backgrounds happy -- *at least until they fall into one of
> the many pitfalls ahead of them.*
>
> *Shouldn’t the entire JavaScript community benefit from `class`?*
> *How to Fix `class`*
>
>    1. *Make class inheritance compositional* similar to the way stamps
>    are composed.
>    <http://chimera.labs.oreilly.com/books/1234000000262/ch03.html#prototypal_inheritance_with_stamps> In
>    other words, change the behavior of `*extend*`, or *deprecate `extend`* and
>    replace it with something like a *`compose` keyword* that can *compose
>    any number of classes.*
>    2. *Deprecate `new`. *`*new*` violates both the *substitution
>    principle* and the *open / closed principle*. The `*new*` keyword is
>    destructive because *it adds zero value to the language*, and it *couples
>    all callers to the details of object instantiation*. If you start with
>    a class that requires `*new*` (all classes in ES6) and you later
>    decide you need to use a factory instead of a class, *you can’t make
>    the change without refactoring all callers.* This is especially
>    problematic for shared libraries and public interfaces, because *you
>    may not have access to all of the code using the class.* You may think
>    it doesn’t do any harm to just call a factory function with the `*new*`
>    keyword, but the `*new*` keyword triggers behaviors that change what’s
>    going on when the function is invoked. If you can’t count on the function
>    to behave the same way for all callers, you can’t predict what the software
>    will do. That’s bad.
>    3. Make sure that *`class` obeys the substitution principle* when you
>    switch from a class to a factory and vise verse. This is an important
>    point, because if callers are counting on any behavior or property of a
>    class, and you decide to change the implementation to a factory, you’ve
>    just broken the calling code. Additionally, if callers are counting on the
>    behavior of a factory, and you switch the implementation to a class, that’s
>    similarly problematic, though as it stands, *there’s no good reason to
>    switch from a factory to a class*.
>
> The third point may be the most difficult, but if we can *catalog every
> possible breaking change* here, there may be some hope for `*class*` in
> the future, assuming we can get consistency baked into the language spec.
>
> If we can’t fix these problems with `*class*`, we should push to *deprecate
> the keyword entirely,* because as it exists today, *`class` is broken and
> absolutely should not be used*
> <https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3>
> *.*
> Catalog of substitution breaks
>
>
>
>    - *The behavior of `this`*. It always refers to the new instance in a
>    class constructor. In a factory function, `*this*` is dynamic, and
>    follows a completely different set of rules. Possible solution: *deprecate
>    `this`* and instead refer to the class or function by name. A major
>    drawback of this solution is that it would break `*.call()*`, `
>    *.apply()*` and `*.bind()*`, unless we *also change their behavior* to
>    override the function name reference.
>    - *`instanceof`*- IMO, *this is broken anyway* because it doesn’t do
>    what the name describes, and from a user’s perspective, *it flat out
>    lies* when you try to use it across execution contexts, or when the
>    constructor prototype property changes. Possible solution: *deprecate
>    `instanceof`*.
>    -
>
> I know I've raised all these issues on es-discuss before and basically
> been told to go suck an egg, but we all want the same thing -- *a better
> JavaScript for everybody*. Being inclusive is practically baked into JS
> DNA. That's how `class` found its way into JS in the first place, even
> though we already had something *much better: A very good system for
> prototypal OO (object literals, prototype delegation, and concatenative
> inheritance via dynamic object extension).*
>
> ~ee
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150304/fc5b1486/attachment-0001.html>


More information about the es-discuss mailing list