Hacker News new | past | comments | ask | show | jobs | submit login
ES2022 feature: class static initialization blocks (2ality.com)
23 points by ingve on Sept 1, 2021 | hide | past | favorite | 63 comments

Before bashing a new feature make sure to read then criticize the actual proposal [1]. I think the proposal makes sense mostly because of private fields (`#x`) since it is very awkward to initialize those fields outside of the class.

[1] https://github.com/tc39/proposal-class-static-block

I've had the displeasure of having to write quite a bunch of JS over the past few years (I'm mainly a system programmer usually). I do think that the JS "standard library" is ridiculously under-featured and makes dealing with even basic arrays or objects much more painful than it needs to be. It's been improving of late but it's baffling to me that it took so long to have something like Object.values() for instance.

That being said I never though that JS was really lacking syntax-wise. "Arrow functions" are a nice shorthand for sure, and "let" should really have been there from the start, but this addition of classes feels really unnecessary and redundant. It just creates more ways to do things you could already do, without obvious benefits and the obvious drawback of making the language more complex.

Since nowadays transpilation is so common place, what even is the point? You already have a rather large selection of other languages to use if you don't like raw JS.

I'm all for improving the existing JavaScript language but that just looks like tacking on a completely different language on top of it in order, I assume, to make it look more familiar for Java/C# coders?

The biggest thing stopping me from using other compile-to-Javascript languages is that almost none of them fully support async-await.

Many of them have some support for async/await but few can handle all cases such as async-await within try-catch and so on.

Scala.js async-await is not part of the standard libary I think and has this limitation: https://github.com/scala/scala-async#limitations.

Kotlin.js might support async-await but I find their JavaScript documentation so confusing/non-existent I can't tell. But also, Kotlin is too similar to JavaScript already anyway. It doesn't have ML-style pattern matching for example.

The Java-to-JavaScript and C#-to-JavaScript compilers both are too underused as far as I can tell. The latter, Blazor, is too focused on ASP.NET for me to be able to understand if I could write it the way I write React apps.

Elm, ReScript, and other ML/Haskell-influenced languages are too niche even though I'm a big Standard ML fan.

I'm not sure what else there is to look at.

And I really don't want to go back to using promises and callbacks.

Clojurescript's core.async is a game changer.

They invented syntax instead of library functions. You don't need array prepend/append functions, when you can write [item1, ...arr, item2]. Some of those syntaxes are quite elegant and I miss those in other languages.

100%. No one dislikes progress, but the JavaScript world is really something else. I have Flanagan's "JavaScript: the definitive guide" 6th edition (that '6' is already a red flag), which predates so many changes in the language that it's essentially an expensive doorstop now. I think of all my technical books probably only the COM and J2EE ones have aged as badly, and yet I bought the Flanagan far more recently than any of those hoary old tomes.

If you feel like having a go at those shinny Windows 11 APIs, the COM one might come in handy.

Yes, junior devs and (and students) "get" classes much better than they do prototypes. This is a Hugh win when you care about productivity.

(Also note, => is not just a different syntax for function)

Death by a thousand features. Very happy to see all the tons of new library work over the years, but really detest pretty much all new syntax extensions, it's morphed JS into the very thing it was never intended to be.

2006 era JS was more than sufficient to build out highly maintainable 100k+ LOC codebases, all this stuff does is introduce new variations of the same functionality for almost no benefit. Javascript is no longer a small language. I think the only surviving practical small language is Lua, and it's only remained small because the hoards are yet to turn up and bulldoze its original design with infinite feature requests

> 2006 era JS was more than sufficient to build out highly maintainable 100k+ LOC codebases

Typescript wouldn't have been created if this was the case.

Typescript was about bringing the culture of static languages to a language design that explicitly rejected it. Large JS codebases already existed in 2006 (Gmail, ..)

As far as I know Gmail used the Closure compiler which can be considered as a spiritual precursor to TypeScript. Also several Google service frontends were in fact based on GWT and written in Java.

Microsoft creates useless things all the time and pushes them until they're finally accepted.

It would be nice to have another long feature freeze with languages adding features on top of JavaScript again. That seemed like a great way for ideas to stew for a while - I went from 'oh finally' on new JS features to 'oh ok'

Why are we slowly turning javascript in java? This seems like a secret Oracle plot

Indeed. JavaScript was supposed to be scheme with its own object property inheritance model. Of course stakeholders co-opted the language and forced the Java style syntax (and name) upon the language. Now most of the syntax additions has been focused on turning it into some kind of C#/Java lookalike so people who don't like functional programming and the object paradigm used in JavaScript can pretend it's just like the OOP stuff they like.

Instead of trying to turn JavaScript into C#/Java, C# and Java should just support compiling to web assembly and provide a good standard library for working with browsers. /rant

I suspect it's because frontend web developers are jealous of other people using and interesting variety languages, so they want to incorporate as many features from those other languages as possible in order to feel like they are getting some variety themselves.

I don't blame them. That's exactly how I would feel!

That, or, it's easier to upgrade to a new JS version + let your transpiler figure it out, than to rewrite your code in some other language that compiles to JS.

I find Java code way more maintainable than JavaScript or TypeScript, to be honest. Especially since Java 11, the language has evolved significantly.

Yep, the fact that TC39 seems to accept just about anything is really concerning to me.

I'm already fighting with people about integer literal separators not being supported in e.g. Vue and stuff, the more that's added, the more the community becomes fragmented, and I'm not convinced that transpiration (I hate that word...) is really paying off for us after all these years.

it's all about acceptance and being wide open

I loved the "good parts” of JS back in 2010. I think I don't need most of this new syntax and yet sometimes have the feeling I'm losing the train when I see this “C#” code base. Curiously, that was the technology I used to work before.

Does JavaScript have regular blocks? There are a number of times where I want to put a small amount of local logic in a block without having to put into an anonymous function called immediately and without moving that logic further up in code.

A specific example of this is doing some small custom callback logic in d3 configuration.

    foo: {
      const f = 1;

    foo: (() => {
      const f = 1;
      return f;
Blocks are common in other expression-oriented languages like Scheme, Ruby, Rust, and Standard ML. I would like to be able to use that in JavaScript.

It does, but they aren't expressions like you're suggesting. They only thing they do is limit the scope of variable defined inside them (assuming let or const are used. var is not effected). There is a proposal for this under the name "do expressions" because the proposed syntax is like this:

    foo: do {
      const f = 1;
(because a plain block would be ambiguous with object literal syntax in some cases).

Sweet, thanks for the pointer.

Kind of.

    let x

    _: {
        let y = 1
        let z = 2
        x = y + z

    y // <- throws

Oh yeah. It just doesn't can't return a value.

So, this, but with more steps?

    function Foo () {
      var x = 1 // "static initialization block"
      return {
        y: 2,
        z: function () { return x + y }
    const foo = Foo()
There's been exactly zero need for the "class" keyword, "constructor()", or that new "#private" thing.

There is one flaw in the design of the language which opens the door to all this nonsense; that's having to write "foo.method.bind(foo)" instead of just "foo.method" when passing "method" as a callback, in order to preserve the value of "this". Although in the example above this is not a problem, as you can write functional code that passes the context explicitly without needing "this".

Another thing I wish JavaScript had kept was the "with" statement.

> Another thing I wish JavaScript had kept was the "with" statement.

Consider the following:

    with (a) {
        with (b) {
            with (c) {
                d = e;
Does this mean:

(1) a.b.c.d = e

(2) d = a.b.c.e

(3) something else

(4) it's impossible to tell?

Correct answer: (4) it's impossible to tell!

One of the worst things about programming is looking at someone else's code (or worse, one's own code) and thinking "WTF did they mean by that?". And that's why I don't like the with statement.

a.b.c.d = a.b.c.e

or a.c.d = b.e, or a million and one other things

> that new "#private" thing

I can agree that classes have been more or less a syntactic sugar, but private identifiers are not. The field name collision is a problem in virtually every dynamically typed language with namespace inheritance and each language has their solutions (see Python `__mangled_name` and Ruby `@instance_var`), JavaScript just happens to have another solution and that alone justifies a separate `class` syntax (since the scope of such private identifiers should be lexical).

I know they're not, that's why I'm objecting to them in principle (even though I use them and they're quite handy). JavaScript already has a solution - just put the private fields in the parent closure where they can't be accessed externally. In my example above, x is a private field, and the constructor function Foo can be asynchronous. The new semantics is a kludge around the supposedly "optional" class syntax sugar.

Remember, once upon a time JS was supposed to be Scheme...

> just put the private fields in the parent closure where they can't be accessed externally

Your approach can't translate the following code:

    class MyNumber {
        constructor(value) { this.#value = value; }
        add(other) { return new MyNumber(this.#value + other.#value); }
The proper solution is to use a non-enumerable property with an unnamed Symbol name, which is possible but cumbersome.

You're not supposed to be able to refer to other.#value, though, isn't that the entire point of a private field?

They are private to classes, not instances. Many languages with private fields do allow access to private fields in other instances of the same class.

Arrow functions replace the need to use `bind` and you can still use `with` just not in strict mode unfortunately.

We just need pattern matching and js/ts is going to be very nice language to use.

Typescript is not Javascript. TS is a language maintained by Microsoft and happens to compile to Javascript but doesn't have to.

Thus, TS should not have any influence over how JS evolves, but unfortunately that ship has sailed it seems.

"TS should not have any influence" I argue it had a positive influence. Do you argue it had an undue influence?

Yes, though less on the language specifically and more on the ecosystem.

Javascript library maintainers should not be berated like they (we) are when types are not added to libraries, for example, but these days it's an unspoken requirement.

Expectancy bloat in the ecosystem is a thing, but it's also democratic. So long as it's an unspoken requirement due to massive usage of typescript, I don't think it's undue influence. I don't always like the change of tides - I guess to a certain degree I say Javascript had an undue influence on my programming. But in other ways it's not undue, as there's clear market conditions etc

Sounds a bit far fetched, adding types to libraries is requirement for what, exactly?

For the users who create snarky issues and demand them.

Github community guidelines explicitly lists bullying and harrasement [0] as violations. It should be enough to mention it once in readme, refer to it, report abuse or block users in case of problems.

Typescript community manages external typings for tons of libraries that don't want to support ts themselves. People can be referred to create type definitions themselves under DefinitelyTyped.

[0] https://docs.github.com/en/github/site-policy/github-communi...

No. Being rude and demanding should never violate community guidelines. That's how censorship starts.

Their bullying and harassment guidelines are very clearly toward ad-hom types of remarks and beyond.

Being on the receiving end of a dick comment about how not having typescript types is unacceptable and how I need to have them for one reason or another is never fun - but, assuming they don't personally attack me as an individual, I'll fight tooth and nail for them to make those comments as it pertains to the community guidelines.

That's not to say I appreciate them or let them influence my maintainership, but they shouldn't be disallowed or cause for punitive action by any means.

Do you happen to have examples of those kind of demands?

I was actually hoping that JS would be moving more and more into FP territory...

Is is with immutable constructs (record and tuple [0]) for example. Current FP story in ts/flow is surprisingly good.

[0] https://github.com/tc39/proposal-record-tuple

Oh, I like that a lot, especially as they've proposed a new JSON.parseImmutable() method as well. The syntax is surprisingly simple (and obvious!) and deep equality and simple comparisons is perfect.

Deep paths look interesting on top of this, the idea is one I like but not sold on it being implementable in any sort of non-clunky way.


    class Foo {}
    const r = func()
    Foo.prop1 = r.prop1()
    Foo.prop2 = r.prop2()
What am I missing? Why do we need all these new syntaxes?

Three words: embrace, extend, extinguish.

That proposal is ugly. I'd rather just use:

    class Translation{
        static get translations(){
            return {blah: things};
        static englishWords(){ return Object.keys(this.translations);}
Since it's all static can't browsers optimize what's already there in this case? I feel like I'm missing something.

There's an observable difference with your getter based approach and the "normal property values" approach in the article:

    const t = new Translation
    // property value that
    // is initialized once
    t.translations === t.translations
    // getters which return
    // new objects
    t.translations !== t.translations
so at the very least the browser can't optimize that behavior away, it needs to create new objects. Besides, ensuring that say, call to `Object.keys` is side-effect free is challenging.

Are you not talking about a class instance? shouldn't all of this initialization happen inside of the constructor?

If we're talking only about static properties it seems more reasonable to me to freeze them upon definition or first use.

The const t = new Translation was indeed a mistake in my example, since we are talking about static properties and not instance properties. But the point still stands, just remove "new" from that example and the same applies, the getter-based approach creates new objects each time.

> it seems more reasonable to me to freeze them upon definition or first use

I agree, and that's why I'd default to

    static translations = {blah: things}
and not

    static get translations() {
      return {blah: things}
the latter does not "freeze" them

the author, thanks to him. has put out a lot of material to gain deep knowledge in javascript. I love js and it has put food on the table. but it's becoming a difficult language to learn / keep up with for people with preference for small languages. yeah ES+ features are beneficial sometimes but adding new syntax just keeps folks on the hamster wheel. python suffers this problem as well, that my pleasure in writing python was in the python 2 days.

I have started migrating from those platforms to ruby / clojure. since things rarely get added to those ecosystems. one can pop in after 20 years and nothing has changed and everything still works as before

> one can pop in after 20 years and nothing has changed

20 years ago Clojure didn't exist and Ruby was practically unknown outside of Japan

    JS in Spring
    Lotus blossoms
    Java clouds rain
    All is lost

I dont get why we need this. I actively avoid classes in JS and dont really see the need for static blocks. Its something Ive never needed.

Id like to see more functional programming concepts implemented before more OOP.

>I actively avoid classes in JS

JS has things like String,Number,Date why not use classes to create your own stuff (say Customer,Product, Transaction) and have your pure functions operate and return well defined objects - for sure it helps with readability and maintainability.

Meanwhile, we still don't have operator overloading. Even Lua has it.

There is a proposal: https://github.com/tc39/proposal-operator-overloading

There are still a couple issues that have to be solved. m2c: I hope that won't get to stage 3, though I like operator overloading in general, I think it's something that will complicate JS even more.

This makes me sad. All of these added features mean it's increasingly hard for more junior developers to break into this industry. The market essentially dictates that juniors learn JavaScript. The status quo of poor interviewing practices is such that juniors are forced to memorise curious quirks of the language, rather than focus on the essentials (like in JS: The Good Parts) to be productive.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact