
Public and private class fields - feross
https://developers.google.com/web/updates/2018/12/class-fields
======
sjroot
This syntax is...odd. It is not expressive and comes off as a "language hack."
The only people who would understand it are those who happen to stumble on
this article.

Frankly, if this is an issue that you are concerned about, you should really
invest the time to learn (and possibly migrate to) TypeScript. Specifically,
see their class documentation which already has support for `public`,
`private`, and `protected` [1]. (Edit: read @denisw's reply regarding TS. I
make this suggestion specifically for TS's compile-time assistance regarding
private field access.)

It actually kind of boggles my mind that V8 engineers sat down and agreed that
this would be a good design decision. Granted, the article says that they are
proposals—is there any way to provide feedback on this? (Edit:
[https://github.com/tc39/proposal-class-
fields/blob/master/PR...](https://github.com/tc39/proposal-class-
fields/blob/master/PRIVATE_SYNTAX_FAQ.md#why-arent-declarations-private-x))

[1]
[https://www.typescriptlang.org/docs/handbook/classes.html](https://www.typescriptlang.org/docs/handbook/classes.html)

~~~
denisw
One important thing to note here is that this goes much further than `private`
in TypeScript. The latter is purely a compile-time illusion. If you write
`private x`, your objects' x property is still publically accessible and
modifiable as far as the JS runtime is concerned. any other. It‘s just the
TypeScript compiler that gives you an error if you access x, and only if it
happens to have the type information it needs to do so (if the object ends up
in an `any` variable, all bets are off).

This means TypeScript's "private" properties also shows up in the return value
of Object.keys() etc. So it‘s a pretty leaky abstraction.

This proposal, on the other hand, tries to solve the much harder problem of of
bringing _true_ private instance variables to JS. This means making it
_impossible_ to access these variables from outside the project. To do this,
they had to introduce a complete new "private namespace" that is different
from the `this.<name>` one, because the latter is already reserved for the
visible-to-everyone properties (messing with that would have serious
compatibility implications). Hence the `#foo` syntax.

~~~
jamestimmins
Does this mean that with TypeScript someone could still see private values if
they used their developer console on the browser, but with this approach that
would be impossible?

~~~
james-mcelwain
Access modifiers in TS are compile time only checks.

This change is not about visibility in dev tools. I'd imagine you'll still be
able to see and mutate private fields in dev console, just like you can in a
Java debugger, etc.

This change is for libraries, so that if someone consumes a TS library, for
example, they can't monkey patch or otherwise touch private fields at runtime.

------
catpolice
First, like the other commenters, I'm not in love with the perlish # syntax.
I'm not sure why it was wise/necessary to add a previously illegal character
as a prefix, rather than adding a "private" keyword, but I'm sure there's some
reason.

Second, the example affords an opportunity to rant about a pet peeve of mine.

"Now ask yourself, how would you implement this class in JavaScript?"

I wouldn't. Listen folks: unless you're doing some weird/temporary debugging
or you need to add some meta-functionality (e.g. logging) to an API that
reeallly can't change, don't write getters/setters. If accessing or setting a
property needs to do computation or have side effects, refactor so that it's
done explicitly by calling a method. Anything that lets you attach invisible
effects to normal syntax is an anti-pattern because it makes your code behave
differently than it appears to, and therefore it's harder to reason about from
the outside.

~~~
azov
Getters/setters are useful.

E.g. maybe you had a class 'Box' with a field 'area'. Later you added fields
'width' and 'height'. What are you going to do? Change 'area' to 'getArea()'
and break the API? Write extra code to ensure that you update area every time
you change width or height? Or write a getter that returns width*height?

I'd say getter is the cleanest option in many cases.

~~~
fsnarskiy
Please for the love of god learn functional programming. this is a primary
example of being stuck thinking there is a RIGHT way to do things according to
Object oriented design practices.

Firstly objects in JavaScript are not classes, they are just your good old
dictionary value store, to an JavaScript object it doesn't matter if u put a
function or a value under its property, under the hood its just a pointer
pointing to a memory location. infact you can dynamically change it!

Also if this code is running in the browser you get ZERO security benefits, i
can still read your values through the debugger.

But it does impart some very significant slow down, especially if you are
calling the getter a LOT.

With Box.area; it literally has to resolve the BOX variable name and jump 1
pointer. with BOX.getArea(); it has to create a new scope and stack for the
function call and there is SO MUCH MORE going on behind the scenes.

The right solution here is absolutely to update your area as the width and
height change.

please learn functional event driven programming if you are going to give
advice on javascript. most of the stuff you learned and got tested on in your
HS and College courses about how to properly write object oriented code does
not apply to javaScript. ESPECIALLY if performance is vital, which it often is
since you interact with user input.

~~~
hombre_fatal
Let's not stoop to the "learn2code" condescension on a forum where we're
talking to fellow developers. The person you replied to had a perfectly
reasonable point.

I use strict FP languages every day and could not figure out what your point
was, and your condescending intro and outro makes it hard to continue the
discussion with you.

------
mkozlows
This is Exhibit N for the "the class keyword is ruining Javascript"
prosecution. If you want a "private variable" you just use a closure.

    
    
      let incrementer = () =>  { 
         let x = 0; 
         return { value: () => x, increment: () => x++ }; 
      };
    

It's like the OO people are determined to forget about the functional concepts
that made JS great.

~~~
21
and now you are defining you whole class inside that return {}. Do you
honestly find this more readable?

~~~
lioeters
I often use a functional style as the OP's above, instead of classes, but I
wonder about performance hit. Calling it often (like many "instances") and
creating new functions every time seem less efficient that inheriting methods
via prototype.

~~~
Roboprog
If I were to create 300 instances of something with actual func^H^H^H^H
methods attached, rather than 3 instances, I would probably look into creating
a prototype and coping with the perils of “this” usage, but otherwise, I’m not
going to lose much sleep about it.

------
Twirrim
Maybe I'm misunderstanding something here.

This reads like Google is now attempting to dictate the direction Javascript
goes in, by implementing proposals before they've become standards, and
shipping them in their browser. Given the dominance of the browser engine, it
effectively sets the direction of the language, particularly if people start
to use it (which they will).

This is feeling like IE6 all over again. We're getting non-standard
implementations of standards, that will further encourage websites to be
"Chrome only".

~~~
magicalist
> _implementing proposals before they 've become standards, and shipping them
> in their browser._

Is that like commenting before knowing how the TC-39 standardization process
works? :P

The proposals are Stage 3. To reach Stage 4 and be complete, there have to be
actual, multiple implementations shipping[1].

As the spec page notes, implementations are also almost done in both
Firefox[2] and Safari[3].

[1] [https://tc39.github.io/process-document/](https://tc39.github.io/process-
document/)

[2]
[https://bugzilla.mozilla.org/show_bug.cgi?id=1499448](https://bugzilla.mozilla.org/show_bug.cgi?id=1499448)

[3]
[https://bugs.webkit.org/show_bug.cgi?id=174212](https://bugs.webkit.org/show_bug.cgi?id=174212)

------
dberg
Maybe I’m old school but instead of private it looks like they accidentally
commented out a variable. # does not feel idiomatic to me.

~~~
sbr464
I agree, also, it definitely doesn’t help readability. That being said, the
underlying private feature is appreciated.

------
mothsonasloth
Since ES6 it seems like the language is having an identity crisis. Trying to
cherry pick OO concepts here and there.

If Javascript is determined to become fully object oriented, then it might as
well just adopt the tried and true concepts in C++/Java for encapsulation.

This just seems like a painful and less verbose way of forcing encapsulation.

I can appreciate Ruby and Python users might not be happy with this convention
but this is going to end up in tears I think, maybe not PHP level but
still...a mess!

------
oaiey
I always understood that some of the JS language quirks are due to their
humble beginnings. Nowadays however I expect professional decision making.

In 2018 I expect that a language which in many other regards follows the C++
language syntax style (semi colon, curly braces, double equality, ...) follow
also expectations like that private fields are declared with a private keyword
and not some strange new syntax form.

Disappointed!

------
becausereasons
I've seen very few people outside of TC39 express a need for "truly private"
fields in JS, and I've seen ever fewer offer positive comments about the
syntax. On the other hand, I've seen a good amount of bellyaching about it. I
think cramming a fairly unpopular proposal (the #) into a genuinely popular
one (class fields) and calling it a day is going to make some folks pretty
unhappy.

...at any rate, I was unimpressed with the sigil when I first saw it, and that
hasn't changed. I've resigned myself to throwing it in the bin of JS features
that I won't be using, like `with`, `eval` and `==`.

------
bokchoi
Wow, the '#' prefix is so ugly.

~~~
ape4
Agree. `public` and `private` keywords would be much better.

~~~
cpeterso
public and private are already reserved keywords in JavaScript strict mode, so
there should be no issue with web compatibility.

[https://mathiasbynens.be/notes/reserved-
keywords](https://mathiasbynens.be/notes/reserved-keywords)

------
drzaiusx11
Here's the issue where dropping '#' syntax is being discussed:
[https://github.com/tc39/proposal-class-
fields/issues/177](https://github.com/tc39/proposal-class-fields/issues/177)

------
ihuman
I don't understand why public fields need the underscore prefix. Aren't all
fields public by default? Why do you need to add a prefix to "hide" public
fields?

~~~
mikewhy
They don't, that's just the example they chose. Prefixing variables /
functions with the underscore has long been a signal for "this is private" in
languages that don't provide the functionality.

~~~
sjroot
This is true, but the whole point of this article is to introduce a new way of
handling private class members.

------
a13n
Can't we just use a private keyword like other languages? Why the # symbol.
Just seems random and confusing.

Also, will there be a way to do protected?

------
ajkjk
If they were going to use a new syntax for private members, why not a minus
sign: class { -privatefield }? At least there's some precedent for that, and
it's already a token in the grammar which currently has no meaning inside a
class declaration.

Also: another poster added that private members should just be closed over in
functions. Well, I don't find it productive to condemn classes as as a
concept, but if JS already has classes being syntactic sugar for a type of
methods, it makes sense that private members would be sugar for closed-over
variables also.

------
atombender
A huge (from my perspective, at least) unsolved problem with the private
members proposal is that you can't use it to build immutable objects, not
realistically.

Without private members I can write something like:

    
    
      class Fruit {
        constructor(props = {}) {
          Object.assign(this, props)
          Object.freeze(this)
        }
        withSize(size) {
          return new Fruit({...this, size})
        }
      }
    

Now I can do:

    
    
      fruit = new Fruit({kind: 'apple'})
      // Later:
      saveFruitToDatabase(fruit.withSize(20))
    

This pattern works great, overall, though it isn't performant. With private
members, you'd think you could declare them and also update objects using
transformations. But private members don't work with Object.assign(),
.entries() etc., and aren't introspectively at all. So you can't write
withSize() to do partial updates without spelling out all the field names,
every time.

Turns out it's quite hard to write robust, performant immutable code in JS.
(Yes, I know immutable.js has Record. However, it's not compatible with
getters and setters.)

------
fsnarskiy
Why?

I feel like the person who proposed this is relatively young and newer to
coding. The reason why i say this is the functionality he is proposing can
already be done using one of the myriad of JavaScript design patterns -
[https://addyosmani.com/resources/essentialjsdesignpatterns/b...](https://addyosmani.com/resources/essentialjsdesignpatterns/book/)

It can already be done in a clean and easy way with a an anonymous self
executing function - (function(){//EVERYTHING HERE IS PRIVATE SCOPE})();

this also has massive added benefits to execution time and variable name
resolutions.

Learn more about variable scopes and how powerful JavaScript is.

Also recognize that JavaScript is much closer to a functional programming
language than a typical Object oriented language. If u are going to try and
make JavaScript objects try and behave like classes that you know and love
from java, you are gonna have a BAD time. its going to take way longer to
code, gonna be hard to maintain and reason about it. Try instead to write
asynchronous event driven code where functions pass data around rather then
data passing functions around.

Also it is really dumb trying to set private/public fields in JavaScript code
that runs in the browser, i can still easily access all the variables through
a debugger or running in a custom environment( like Selenium).

and if your concern is about keeping that data safe in a server environment..
well then your really shouldn't rely on private/public to keep you safe and
put in actual user role management and access at the application level, and
one of the best way to do that is utilize a graph database to store and
resolve complex user roles and permissions in your application.

~~~
BillinghamJ
Making classes (or equivalent) in the way you propose - ie "make use of
closures for private instance fields" \- does not work well. It requires
allocating a new copy of each of the class's methods every time the class is
instantisted, working around the prototype system.

If we're arguing about crappy misuse/abuse of JS and "doing it wrong", surely
this is far worse?

~~~
fsnarskiy
what i was demonstrating by showing that design pattern is that there is
already an easier and clean way to do private variables and scopes.

BUT if you want to talk about its speed and efficiency here we go:

It very intentionally trades memory space for faster variable resolution.

plz read this article - [https://www.toptal.com/javascript/javascript-
prototypes-scop...](https://www.toptal.com/javascript/javascript-prototypes-
scopes-and-performance-what-you-need-to-know)

he has some test code at the end that compares the speed of resolving
function/variable names in the local scope compared to going up the prototype
chain. in his case its about 8 times faster in the local scope, but does in
fact require a local copy of the variable.

I initially had to learn and understand JS variable resolution when i was
writing a server to process Google Analytics data from our customers accounts
to get some valuable business insides from the data.

The beauty of a self terminating function is that it creates a scope that is
not even related to the global scope, if the variable name is not found in
that scope it does not start going up scopes, and the scope itself is very
clean and not polluted (unless you pollute it yourself) so its faster to
resolve your variable names, vs an object with a long prototype chain. keep in
mind that in the article above the prototype chain would get slower and slower
the more methods and variables you added.

Just using a self invoking function as a wrapper for my array addition sped up
my code 7 times. along with a bunch of other variable lookup optimizations i
was able to process a GB of data per second on my T2 micro aws server with 1
GB of memory.

In the modern day era of computing i am also very confident making trade-offs
for using more memory via copying that function to be closer in memory when i
need it.

Also keep in mind that when it comes to low level cache hardware (like your
CPU cache) its going to take advantage of the function actually being close in
memory to the object, as well as probably being accessed when the variable is
accessed ( taking advantages of temporal and spacial locality) when the object
you are trying to use gets loaded into memory; it's likely to also load the
function into the cache with it, and then you don't have to wait on all the
cache misses as it traverses up the prototype tree.

OFCOURSE there will be some case somewhere where that object function for some
reason takes up a huge amount of memory and its more efficient to store it in
the prototype, but that will be highly unlikely.

But this kind of design pattern:

var collection = (function() { // private members var objects = [];

    
    
        // public members
        return {
            addObject: function(object) {
                objects.push(object);
            },
            removeObject: function(object) {
                var index = objects.indexOf(object);
                if (index >= 0) {
                    objects.splice(index, 1);
                }
            },
            getObjects: function() {
                return JSON.parse(JSON.stringify(objects));
            }
        };

})();

Is called a module pattern and is used A LOT... like really A LOT in
javascript, most of npm packages are wrapped like this for example, because it
gives them a blank scope and they don't have to worry for what lives outside
that function.

[https://medium.com/@tkssharma/javascript-module-
pattern-b4b5...](https://medium.com/@tkssharma/javascript-module-
pattern-b4b5012ada9f) \-- just read through this

This is a tried and tested pattern. I didn't make this up myself lol

That's why i initially suggested that the developer is probably younger and
less experienced, if you work with JS for the last 5 years you are almost
guaranteed to run into this.

~~~
BillinghamJ
Bear in mind, in the cases you refer to, the inner functions should only be
allocated once - when the file is loaded.

The issue is when you use this pattern for initialising what are effectively
classes. Then every time you use `new`, the functions have to be wrapped
again.

~~~
fsnarskiy
Again you are saying the "inner functions should only be allocated once",
there is no rule or book in CS that states that as the absolute truth. It is
however a concept that would be taught in a typical OO programming class.

Idk where exactly you were taught this, or if you did not read my whole speil
about that being memory/cpu tradeoff.

i know i put up a wall of text, but if u have time to read one article on JS
from all of this it is this article - [https://medium.com/javascript-
scene/common-misconceptions-ab...](https://medium.com/javascript-scene/common-
misconceptions-about-inheritance-in-javascript-d5d9bab29b0a)

------
horstschneider
Private fields in ECMAScript now look like commented out class attributes in
Python. Cumbersome for someone who unfortunately has to program in both (guess
which of those two makes it unfortunate :D)

------
shiado
How does this work with JSON.stringify? Could I convert an object to a string
and then scan the string to get the value of the private fields? (I am not
great at the details of JS correct me here)

~~~
lioeters
Looks like JSON.stringify will not reveal a private field, based on this line
from the spec:

"What do you mean by "encapsulation" / "hard private"? It means that private
fields are purely internal: no JS code outside of a class can detect or affect
the existence, name, or value of any private field of instances of said
class.."

[https://github.com/tc39/proposal-class-
fields/blob/master/PR...](https://github.com/tc39/proposal-class-
fields/blob/master/PRIVATE_SYNTAX_FAQ.md#what-do-you-mean-by-encapsulation--
hard-private)

------
camgunz
Accessing it outside its class body shouldn't be a SyntaxError.

~~~
Twisol
It makes me wonder what would happen if they tried `counter[“#value”]`.
Hopefully still an error, but also hopefully not SyntaxError, because it
certainly isn’t one.

------
danShumway
Am I the only person who's really stinking annoyed that Chromium is just
randomly shipping features that haven't gone through the W3C? Didn't we learn
anything from the early days of CSS and browser flags? Didn't we learn
anything from flexbox?

Don't call it a proposal if you already have plans to ship it. Private fields
haven't been accepted as a standard yet. They _should not_ be shipped except
behind a browser flag.

~~~
BillinghamJ
First, the definition of JS has nothing to do with W3C. (Also for HTML, nobody
really cares what W3C thinks anyway)

Second, the rules of how JS is standardised is that changes are actually not
allowed to go into the final spec until at least two major players implement
support. So this is _exactly_ how it's meant to be done.

A substantial amount of discussion and consideration has already happened
prior to this point, as well as implementations into transpilers (eg Babel)
which people have been using for some time.

This comment explains a bit further:
[https://news.ycombinator.com/item?id=18676717](https://news.ycombinator.com/item?id=18676717)

~~~
danShumway
Implementing support is not the same thing as turning a feature on by default.

The TC39 process states that limited spec changes may still occur after a
stage 3 proposal. Until a proposal reaches stage 4, it should be hidden behind
a settings flag. This helps us prevent situations like flexbox, where users
code against a specification and find out later that their code has broken in
subtle ways.

> _as well as implementations into trans pourers (eg Babel)_

Which is meaningless. Babel is not a part of the standards process, they're
free to ship anything they want. Encouraged, in fact, because that provides
more in-the-wild usage data.

~~~
BillinghamJ
Fixed the autocorrect error.

It is super meaningful - it helps identify potential issues in real world use-
cases and it creates effectively a "market" of people already using the
feature who want to be able to use it without transpiling. It's also pretty
rare that the implementation changes meaningfully after implementation into
Babel from around stage 2.

~~~
danShumway
I should have used a more descriptive term than meaningless.

Transpiler implementations do not count as hard restrictions on whether or not
a proposal can change in the future. They're allowed to do whatever they want
-- the fact that they create demand is very useful, but should not be seen as
a restriction on whether or not the standard can evolve past that point.

"It's also pretty rare" is exactly the problem. They _can_ change. When
browsers ship a non-standardized behavior that's on by default, they are
effectively cementing it. It is very hard to correct a broken implementation
that is live, in the wild, on normal sites. The fact that these changes are
rare doesn't make it less important to follow the established process. Stage 3
is one last safety check before the feature gets turned on for everyone.

What is the point of having a stage 3, if browser makers treat it as
equivalent to stage 4?

------
RandomInteger4
What problem do private fields solve?

~~~
jarvuschris
When you're building a component that will be used as part of larger
applications, possibly written by other people, declaring things as private
lets you cordon off internal parts of the component so that it's safer to
change them in the future without breaking the applications using it

Someone trying to make an application work that is looking at your classes in
a debugger is going to poke at whatever inside details they can see that helps
them get their job done quicker. Now if you change that their application
breaks when they update. Not good for anyone.

Privates make it easier for component developers to get things done without
worrying about breaking users, and makes it easier for users to upgrade their
components without it breaking as much

~~~
RandomInteger4
Wouldn't it be simpler to have a Terms of Service that voids the warranty of
code being misused by bypassing the given API?

I mean, not every software problem has to be solved with software.

------
vikingcaffiene
Not gonna mince words. The hashtag syntax is hot garbage. If you need this
kind of functionality just use a super set language like TypeScript. It looks
and behaves exactly like you would expect.

~~~
BillinghamJ
Although TypeScript is intended to be a superset of JS, it isn't always. There
have been occasions where JS features were not usable via TS due to existing
incompatibles/differences in implementation of new JS syntax. Really it is a
different language.

------
kevindqc
The JS syntax seems to become weirder and weirder to me lol. ::, |> and now #

~~~
BillinghamJ
That's the unfortunate reality of web compatibility ‍️

------
21
Why not use the private/public keywords like in TypeScript?

Was this # thing voted by the community or something?

~~~
mathias
The article links to the explanation by the proposal champions:
[https://github.com/tc39/proposal-class-
fields/blob/master/PR...](https://github.com/tc39/proposal-class-
fields/blob/master/PRIVATE_SYNTAX_FAQ.md#why-arent-declarations-private-x)

------
jeklj
I am not sure it is possible any longer for JS to avoid getting larded up with
a bunch of features from other languages, due to the popularity it has
achieved. But one of the things I used to like about it was its relatively
constrained feature set. (I would almost say simplicity, but some of the
wackier aspects of the language prevent me from going that far.) Now we have
things I’m not sure anyone even uses like Symbols.

(Please don’t tell me I can just constrain the code I write to the subset of
features I care about. We all read more code than we write.)

------
randomsearch
To reiterate what others said, choosing an unusual syntax that is non-obvious
motivated by a synthetic example to solve a problem already solved by many
other languages in better ways almost raises the suspicion that we’re
deliberately trying to make JS even worse... to kill it? Or is this just
indicative of the process that got us to this mess in the first place?

A more reasonable policy for JS would be to (a) contain it - no more changes,
no more extending its use outside of its current surface, (b) the development
of an alternative approach that would - crucially - support competing
languages so that we don’t get stuck in this way again, (c) gradually
shrinking eliminating JS with a goal to complete retirement.

~~~
BillinghamJ
You know a lot of people actually like JS right?

The second paragraph you wrote seems to indicate that you believe people only
use JS because they're forced to - since browsers don't support anything else.

That's not the case. People choose to use JS in all sorts of places where they
could make use of many other options.

Development of the JS spec is complicated somewhat by the strict rules of web
compatibility. But imo the benefits of that compatibility guarantee far
outweigh the downsides. Having slightly ugly/unusual syntax is not a major
issue.

~~~
randomsearch
You’re right, I have assumed that people only use JS because they have to.
Happy to be convinced otherwise - please respond:

Other places - I guess you mean Node, and VS Code and Electron etc? I’ve
always assumed that was either because of the available skill set (web devs
know JS, so make their tools on JS) or to reuse JS on the web and elsewhere.
So not because it’s a good language, but because people know it or solutions
exists in it.

Is that what you meant?

To me, Node (for example) makes sense only if your devs don’t know other
languages or you want to hire cheaply - I’d always go for a “proper” (better
designed, more stable, elegant, robust) language on the backend given the
choice.

~~~
BillinghamJ
I am not - and have never been - a front-end or browser JS developer. I did
not have JS skills to bring over, and yet Node was my preferred language for
writing commercial applications for many years. Now more on the Go side, but
still making plenty of Node applications.

It works fantastically well in IO-bound applications for such a large number
of reasons. It's not far off the most lightweight option for making simple
systems, the ecosystem focusses very much on composition of smaller libs than
using hefty frameworks, its single threaded nature is actually a _huge_
benefit (though you wouldn't want to use it for CPU-bound work).

There's also the fact that JS these days is essentially a completely different
language from the JS which browsers supported 5 years ago. I started using
Node before those changes, but it has only become better with them.

I think you'd be surprised how common this exact story is. A lot of Node
engineers are gradually moving over to Go, but it's still a great system in
its own right.

Also, in reality, if you're a pre-existing browser JS developer, you're
actually going to be bringing a load of baggage which will not help you get
used to Node. I'd almost say you're better moving to it if you don't already
know the lang.

