Live data from Hacker News

How to rewrite classes using closures in JavaScript

gaurangtandon.com

41–50 of 58 posts

Re: How to rewrite classes using closures in JavaScript

#41
This is how everyone wrote classes in JS[1] before the class keyword was added. Most people didn't want to futz with constructor functions and prototype chains. Eventually a class keyword was added to the language.

"Having to write:

this.progressBar.addEventListener(this.handler.bind(this)); is much worse compared to:

progressBar.addEventListener(handler);"

If handler is declared as:

class Foo {

  handler = () => {
    // handle
  }
}

Then there's no need to bind it to this when using it later.[2]

You can just write

this.progressBar.addEventListener(this.handler);

Additionally if handler doesn't reference any other class properties and isn't referenced outside the class, it can also be a function in the module.

class Foo {

  constructor() {
    this.progressBar.addEventListener(handler)
  }
}

function handler () {

  // handle
}

1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

2. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

Re: How to rewrite classes using closures in JavaScript

#42

Earlier quoted context omitted.

classes have the # prefix for private members and TS has the private keyword. If you don’t want to go with classes, not exporting a value or a function from a module is almost the same. The rest can be done with closures.

Is there a “canonical” example of a module that is written well and uses what most would consider best practices? When looking through npm modules, there still seems to be a variety of approaches.

Canonical? Best practices? Not that I’m aware of. Not in Node.

Deno has some ideas though:

> Don’t import any symbol with an underscore prefix: export function _baz() {}.

> Don’t link to / import any module whose path […] Has a name or parent with an underscore prefix: _foo.ts, _util/bar.ts.

https://deno.land/std@0.204.0

Re: How to rewrite classes using closures in JavaScript

#43

Private and readonly properties are 100% possible on classes, and you don't even need the newfangled private property syntax! You can roll it your own easily, and all you need is WeakMap. Unfortunately nobody seems to understand what weak maps are for... Here's the example code: https://gist.github.com/conartist6/95f5be3488b2ae6c0dfd9a886...

There are truly private properties available now.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

Re: How to rewrite classes using closures in JavaScript

#44

Lord, no, please, please don't do this in any codebase that anyone else has to actually look at/contribute to. Other commenters have pointed out some of the technical issues with this implementation (all instances get a copy of every method, no prototype-based type checking, non-standard use of "init" vs new, etc.) But the much more important issue is that you did not make life easier for other developers looking at…

> non-standard way of doing things

Fwiw this exact pattern was the idiomatic defacto standard in JS before classes were added to the ECMA specs. This will be extremely familiar to anyone with a reasonable number of years of experience in JS.

That's not a good defense in itself: things should ideally be familiar to beginner devs, but that's more of a debate on what's being taught in current JS education than on what's "better".

i.e.: if this were a better pattern (it's not), lack of familiarity wouldn't be a good argument against it.

> with very little, if any, real benefit.

In fairness the article spends most of its time laying out the proposed benefits, so stating that there's no benefit without addressing its arguments is a little lazy.

Some of the arguments are inaccurate (private fields have been widely supported since 2021) but it's true that ECMA's class implementation is riddled with issues & exploring alternative patterns is worthwhile.

Even before ECMA added classes, I never liked this pattern: it seems a clumsy attempt to shoehorn classical inheritance -esque arrangements onto a prototypical language without ever putting forward reasoned arguments for the benefits of doing so. This article at least tries to put forward arguments about why fake shoehorned classical inheritance pattern is better than the "standardised" shoehorned classical inheritance pattern, but in reality both are bad.

Re: How to rewrite classes using closures in JavaScript

#45

The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures." Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He careful…

Yeah, I'm losing my edge. The kids are coming up from behind. I'm losing my edge. I'm losing my edge to the kids from Stanford and from CMU.

But I was there. I was there in 2008. I was there at the first Expo show in IRC. I'm losing my edge to the kids whose fingertips I hear when they get on the decks. I'm losing my edge to the Internet seekers who can tell me every member of every JS design committee from 2008 to 2018.

I'm losing my edge. To all the kids in typescript and wasm. I'm losing my edge to the bootcamp Brooklynites in little jackets and borrowed nostalgia for the unremembered nineties.

I was there. But I was there. I can hear the fingers every night on the decks. I was there in 2008 at the first search engine collapse. I was working on MVC frameworks with much patience. I was there when Ruby started up test-driven development. I told them, "Don't do it that way. You'll never make a dime."

I was there. I was the first guy playing dynamic dispatch to the php kids. I played it at phpclasses. Everybody thought I was crazy. We all know. I was there.

I've never been wrong. I used to work in the dev store. I had everything before anyone. I was there in the C# linq booth with Anders. I was there in Glasgow during the FP clashes. I woke up typing on the beach in frontpage in 1998.

But I'm losing my edge to t-shit clad people with proprietary ideas and more investment. And they're actually really, really nice.

I'm losing my edge. I heard you have a compilation of every good quine ever written by anybody. Every great patch by Linus Torvalds. All the flamewar hits. All the sourceforge tracks. I heard you have a floppy of every 90s asm rpg on German import. I heard that you have a white label of every seminal Gang-of-Four techno hit - 1995, '96, '97. I heard that you have a CD compilation of every good '60s fortran book and another box set from the '70s.

I hear you're learning vim and lifetimes and are throwing your c compiler out the window because you want to make something safe. You want to make a web app.

I hear that you and your team have deleted your .NET repos and installed npm packages. I hear that you and your band have deleted your npm packages and installed F#.

I hear everybody that you know is more relevant than everybody that I know. But have you seen my records? Wirth, Peyton-Jones, Dijkstra, Fortran, C, C++, D, R, Haskell, PHP, Python, Scala, Java, C, ... malloc, gcc, #include, while(*s++), ...

You don't know what you really want

Re: How to rewrite classes using closures in JavaScript

#46
This is so weird! It seems like the author mostly just hates the aesthetics of classes but actually likes structuring code in an OOP way. Good on them though, I've never written a class in my life personally, so at least they're on the right path. :)

Re: How to rewrite classes using closures in JavaScript

#47
> `this` is awkward

`this` is excellent for maintenance (being able to easily distinguish between instance variables and other things is very valuable), and generally good for performance (more subjective and nuanced involving runtime and memory and consideration of how often you instantiate and how often you use and I won’t provide any citation).

> No private properties

This is where the article falls down; and everything after it is suspect. Because all browsers have supported private class fields since mid-2021. For almost all purposes, this has fairly recently become good enough to depend on. Private class methods is right on the verge, since Safari added it in 15.0 (September 2021) rather than 14.1 (April 2021) like private class fields; I’d say that most purposes can safely depend on this now.

Reference: https://developer.mozilla.org/docs/Web/JavaScript/Reference/...

> No readonly properties

  class MyClass {
    static get prop() {
      return 123;
    }
  }
  MyClass.prop = 456;
In strict mode (e.g. ECMAScript Modules), that last line will throw a TypeError; otherwise, it will silently do nothing. (That’s how non-writable properties work in JavaScript.)

The more-efficient-but-less-friendly-to-some-tooling alternative is placing this after the class:

  Object.defineProperty(MyClass, "prop", { value: 123, writable: false });
> Poor bundler optimization

There is some legitimacy to this claim, but it’s entirely because all relevant tooling is surprisingly primitive. And most of the places where it seems legitimate, your replacement will suffer from the same issue.

But a lot of it is actually not legitimate: if you only used private fields, they would be optimised, detected as unused, &c. in all these ways.

The example is also a bit dubious in points like the AVERAGE_HEIGHT_FT field on Dog: writing it that way rather than as private or (typically my preference) as a const adjacent to Dog implies it’s supposed to be public (and the naming of these static fields supports this).

And .height and .weight being part of the public interface? I expect that to be a feature, not a bug. .weight is not unused, unless you check the whole code base and find it so. (And the surprisingly-primitive tooling can’t do that whether you use a class or an object.)

> Closures to the rescue! […] Immediately, we see several advantages:

Four are listed. Numbers 1, 2 and 4a are broadly or completely false because you just needed to use private fields. 4b has also been addressed (readonly is just spelled using a getter function).

The only interesting claim is the third:

> 3. We have lesser code bloat thanks to removing this. and Dog. prefixes.

You probably have a smaller bundle size, but it wasn’t bloat: by removing it, you’ve vastly increased memory usage requirements and normally slowed things down, because now instances share almost nothing (only static fields), instead of almost everything (all except for instance data).

—⁂—

All up, I say: try using private properties for a while, and measure various aspects of performance: actual bundle size savings, plain and gzipped; memory usage; and performance if you have any places slow enough to actually measure anything. Then come back and we can consider the topic anew.

Re: How to rewrite classes using closures in JavaScript

#48

What is the best and most modern way to write a module where you have public and private functions and data?

I'd say you might need to be more specific with your phrasing. If you want replicate more class-like behavior (but without actual class/inheritance information): - Some type of exported constructor function - The function instantiates an instance of data internally that is possibly mutable (let vs const) - That function returns an object/interface of functions that have access to that data by virtue of it being defin…

Context is if I am writing a node app from scratch in JavaScript, I’d like to use all the best practices for laying out my code.

With so many options, it’s hard to know what you’re doing is “right” and to setup the appropriate lints and whatnot.

Re: How to rewrite classes using closures in JavaScript

#49
> The this prefix is mandatory for every instance property, which increases code bloat

Stopped reading there. Whenever someone displays this mentality of "less code is better, every character above the minimum necessary is bloat" I immediately assume they've never had to read complex code written by someone else (or even their past selves) in their life.

Re: How to rewrite classes using closures in JavaScript

#50

Lord, no, please, please don't do this in any codebase that anyone else has to actually look at/contribute to. Other commenters have pointed out some of the technical issues with this implementation (all instances get a copy of every method, no prototype-based type checking, non-standard use of "init" vs new, etc.) But the much more important issue is that you did not make life easier for other developers looking at…

> non-standard way of doing things Fwiw this exact pattern was the idiomatic defacto standard in JS before classes were added to the ECMA specs. This will be extremely familiar to anyone with a reasonable number of years of experience in JS. That's not a good defense in itself: things should ideally be familiar to beginner devs, but that's more of a debate on what's being taught in current JS education than on what's…

> Fwiw this exact pattern was the idiomatic defacto standard in JS before classes were added to the ECMA specs.

No it wasn’t. Some people used it, to be sure, but more used the prototype chain properly, storing data in `this` and all that.

Post reply on HN