V8 release v7.2
v8.dev
v8.dev
For those who don't know private class fields add the weird "#" character for defining them inside the `class {}` block.
class IncreasingCounter {
#count = 0;
get value() {
console.log('Getting the current value!');
return this.#count;
}
increment() {
this.#count++;
}
}
This looks way worse than having what TypeScript did by introducing `public` / `private` keywords. Is there any body
which votes for es-next features such as this one? I wonder how the "#" private field popped up in Chrome specs.1 : https://developers.google.com/web/updates/2018/12/class-fiel...
2 : https://developers.google.com/web/updates/2018/12/class-fiel...
https://github.com/tc39/proposal-class-fields/issues
There's at least three specifically about the #:
https://github.com/tc39/proposal-class-fields/issues/177
https://github.com/tc39/proposal-class-fields/issues/149
https://github.com/tc39/proposal-class-fields/issues/142
Remember to be constructive or just give a thumbs up to comments you like.
This is not to say that there aren't other viable solutions which would avoid the use of the current proposed syntax, but how do you weigh the advantages of avoiding a subjectively "ugly" syntax against the many other, much more concrete concerns that the TC39 has to evaluate when choosing a proposal? Should "the reflexive reaction of many devs when they first see the feature (without taking any time to try it out) is that the syntax is ugly" just be an automatic veto? If not, how strongly should that concern be weighted? Should an otherwise inferior proposal be accepted instead merely because the syntax is slightly prettier?
Ultimately, I think you need to have a person or group of people in authority who will look at all available options and make an informed decision, even over the objections of the uninformed majority. Right now, the TC39 _is_ that informed authority.
Namely they voted for encapsulation / hard-private [#33] as well as comparing private properties of two classes [2], which as a result makes the only possible syntax the one with the `#`.
Unfortunately this comes at the price that once browsers implement this it will become history ( same as the `with` statements [3] and `labels` with `continue` [4] ). Because of backwards compatibility these will never be removed.
Maybe in 2022 TC39 will decide that "use static-type;" will be a mode, such as strict mode, where you can write statically typed JS ( great idea, btw ), where this proposal will make no sense. Another "use class-inheritance;" mode would also finally end the abuse of prototype inheritance with syntax sugar. In this future the `#` will be just a huge annoyance.
#33 : https://github.com/tc39/proposal-private-fields/issues/33
2 : https://github.com/tc39/proposal-class-fields/blob/master/PR...
3 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
4 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
[1]: https://github.com/tc39/proposal-class-fields/issues/177#iss...
I mean whenever I see # I thought I am either on tweeter or the code was commented out.
It also didn't help that library authors are over-represented in the committee relative to the regular users of the language.
Ultimately, I agree that we need to have an authority to make the final decision and it helps if it's informed, but we need to be aware that it's not going to be perfect and it will have blind spots, so perhaps a way for people to veto those decisions should exist.
Thank you for putting that into words. That's what I was initially trying to express when I talked about "mudding the water", but the second paragraph sorta took my train of thought in a different direction.
I can't help but think that if there weren't so much discussion about the syntax, and consequently so many people willing to throw their support behind any alternative proposals merely because they avoid that particular syntax, some of the more substantial concerns about this proposal might have gotten a bit more consideration due to the higher signal-to-noise ratio.
A particularly important member to point out is the JS Foundation, which sends devs of babel, lodash, etc to TC39 meetings as well.
IMHO "No solution" would be better than "bad solution" in this case.
1 : https://github.com/tc39/proposal-class-fields/issues/100
class Example {
#myVar = 100;
getPrivateMyVar() {
return this.#myVar;
}
getLocalMyVar() {
return this.myVar();
}
}
const example = new Example();
example.myVar = 200;
console.log(example.getPrivateMyVar()) // 100;
console.log(example.getLocalMyVar()) // 200;
I agree the syntax is terrible. But you need to be able to distinguish between the own object property myVar and the private field myVar.example.myVar = 500 should throw an access error.
You don't need to distinguish because you shouldn't be shadowing private variables.
That removes a major benefit of private fields (which is to be able to change implementation details without breaking the public interface)
I don't think anyone really loves the syntax, but there doesn't seem to be much of a better way of doing it.
It's not possible to have current code that works like that, so defining new properties for that code (that it can only be used inside the class as a private field) isn't breaking anything.
In other words, outside of the class implementation, `example.#myVar` is always an error, regardless of the existence of a private field or not, and it will always be an error with or without this proposal. And inside the class it's only legal if the private field was defined in the class.
Dynamic property access isn't allowed for private fields, so it kind of side steps that whole issue.
https://github.com/tc39/proposal-class-fields/blob/master/PR...
On one hand I follow a more functional programming style so this won't affect me but on the other I worry about trying to understand other people's code (especially very serious programming [tm]).
>If they are different it's very inconsistent and IMHO a lot worse.
It is called out in the link I posted above as a "downside", and I agree that it sucks, but there aren't any other good options for this without breaking compatibility with current code.
>I worry about trying to understand other people's code
Luckily there is one big benefit to this that makes the impact of the weirdness surrounding the naming much less awful. Because of the "hard encapsulation" that the private fields have, they are literally and explicitly un-viewable, un-accessible, and un-usable outside of the class body.
So the amount of complex meta-programming and many of the reasons you'd want dynamic property access in the first place (iterating, monkey-patching, using arbitrary strings as keys, computed keys, etc...) aren't applicable, and if you really want those features but also with privacy, you can nest a plain object as a private field and work with it that way just the same.
So it may have some gotchas, but because the proposal is intentionally restricted as much as possible, and it explicitly disallows a lot of the metaprogramming that can be common and it only allows a very simple lookup syntax, the weirdness is very well contained, and at worst will be confined to between the opening and closing brace of a `class` statement.
That's not an extreme hypothetical, it was a real example from someone trying to use this feature. They had tried to emulate private fields with an underscore before, people started using the private variables, and now they want to refactor while keeping the current "private-turned-public" variable working the same.
I'm sure you can come up with answers for most of those things, but will they really be easier to understand than the current proposal? I'd hate if I have to look around in the class any time `this.anything` happens to see if it's private, public, shadowed, a getter/setter, etc...
`this.#myVar` is explicit, it's obvious, and even if it's ugly as a sin, you know it's not doing something "normal" right away.
Imagine you are using a library and extending it adding a few properties for usage in your app, and one patch upgrade your app breaks because the `example.tag` stops working because the library decided to add `this.tag` as a private variable.
Imagine React decides to use this, and they add a private `this.internalState`, but all react components are extended from that. So now all react components can't use `this.internalState` and if the react team ever changes the name of the private variables, then it is a breaking change and can cause code to stop working.
You can say "well don't do that" all you want, but a good portion of the web uses techniques like that, and breaking code because you feel they are doing it wrong doesn't make a healthy ecosystem.
What are some of the other languages that interpret "private" as never exposing the existence of that field to a public consumer? Most languages I've used don't expose the _value_ of the field...
See [0] for some info straight from the proposal, but the gist is doing it this way makes it so that you never have to worry about name collisions for private fields. Subclassing, superclassing, and object "monkey patching" all can continue to work as-is with no changes, and none of them have to worry about the existence of private fields complicating implementations, and the classes with private fields can feel free to change and rename them as much as possible, even within patch versions of libraries, since they cannot impact any code outside of the class under any circumstances. That's a pretty powerful guarantee to give which GREATLY simplifies working with them.
If I want to extend the react component class, I don't need to worry that my `#component` private field is going to break if an update to the react component class adds their own `#component` field, or if a user of my library tries to do something like this:
const fancyObj = new FancyObj()
fancyObj.tag = Symbol('tag')
Which I'll admit to having used in some cases where I want to tag a bunch of otherwise identical objects which will be thrown into an array and then be able to easily pluck out the ones I tagged later. Without true encapsulation, that code above would break if they changed the private interface to use the `.tag` field internally, and I would have no idea because it is consitered a private interface and wouldn't need to be documented or maintained across versions.[0] https://github.com/tc39/proposal-class-fields/blob/master/PR...
There is nothing addressed here that can't be solved with composition. This seems like an odd step for an aspiring functional language to take...
Some of that is pushing it in a functional direction, some in a "classic OOP" direction.
Much like classes in the first place, this is a feature added to the language to stop the proliferation of stop-gap solutions and standardize on one implementation for most.
Sure, I won't be using this feature, just like I won't be using classes at all in most cases (React components being one exception), but many will, and if this isn't added, they will continue trying to solve problems with solutions which are more complicated, slower, and buggier than this. All the talk about how it's not the "right" solution doesn't matter.
To use an over-the-top metaphor: People are jumping out of planes, and you aren't going to stop them, so the very least you can do is give them a well tested parachute and a map of where to safely land.
class Base {
private int field;
Base() {
this.field = 1;
}
void baseMethod() {
System.out.println("Base.field = " + field);
}
}
class Derived extends Base {
private int field;
Derived() {
this.field = 2;
}
void derivedMethod() {
System.out.println("Derived.field = " + field);
}
}
public static void main(String[ ] args) {
Derived d = new Derived();
d.baseMethod();
d.derivedMethod();
}
It prints: Base.field = 1
Derived.field = 2 class Base:
def __init__(self):
self.__field = 1
def baseMethod(self):
print("baseMethod " + str(self.__field))
class Derived(Base):
def __init__(self):
super().__init__()
self.__field = 2
def derivedMethod(self):
print("barMethod " + str(self.__field))
d = Derived()
d.baseMethod()
d.derivedMethod()
And it prints: baseMethod 1
barMethod 2
Ruby doesn't really have fields that are private to subclasses, but Ruby also embraces monkey-patching, so that's not entirely surprising."Having a private field named x must not prevent there from being a public field named x, so accessing a private field can't just be a normal lookup. This is only an issue in JavaScript because of its lack of static types. Statically typed languages use type declarations to distinguish the external-public/internal-private cases without the need of a sigil. But a dynamically typed language doesn't have enough static information to differentiate those cases." [0]
There are also performance concerns to changing the lookup logic without the sigil. More info in the link.
0: https://github.com/tc39/proposal-class-fields/blob/master/PR...
Sure, you say, that's because in PHP a public property and a private property cannot have the same name. Correct. Why is this a requirement? What problem is this trying to solve? In e.g. older Python we would use the convention of a leading underscore to indicate that a property is private, even if technically one _could_ access it publicly it was understood that is a bad idea to do in practice. It's not a security issue - in every language one could read the "private" properties by some method or another. Reflection, for instance. Or, back to PHP, by casting the object to an array (I did mention that PHP is the canonical hate-it dynamically-typed language).
The problem is that it seems a "private by convention" system isn't private enough for many library authors, who are finding their private internal methods being used regardless, and then after some time becoming ossified leaving the maintainers in a tough situation. Support the "private" interface even though they shouldn't have to to avoid breaking a significant number of users, or break the "private" interface and leave many developers with code churn.
Regardless of where fault lies (often times it's one library using the private interface of another library, and the developer gets caught in the middle), this is a bad thing, so people began workarounds like complicated closure systems or weird-looking interfaces which emulated true private fields without having support for them.
This started becoming popular enough that it was decided that adding them to the official syntax would be better, and unlike in PHP or other languages, there is no way around it here. Serializing or casting the object doesn't expose private fields, and there is no reflection methods or anything that would allow it. They are truly private.
I personally share your feelings that this isn't an issue that needs solving (i'm of the opinion that the lack of easy truly private properties had a non-zero impact on the rise of javascript, and the ability to tweak private interfaces in your code is a net benefit, even if it gives developers rope to hang themselves with), but the pain felt from it is real for many, and the workarounds they were using were not only slow and difficult to use and maintain, but also very hard to understand, and this alternative is a much better solution in my opinion.
So while I most likely won't be using them, I reluctantly support them being added to the language.
[0] https://github.com/tc39/proposal-class-fields/blob/master/PR...
> Having a private field named x must not prevent there from being a public field named x, so accessing a private field can't just be a normal lookup.
Why do classes support public/private fields of the same name in the first place? This seems fraught by nature?
class A {
#x = 100;
}
const a = new A();
a.x = 200;
On top of this, you should be able to define private variables and methods in derived classes without interfering with the base class private properties: class A {
#x = 100;
getA() { return this.#x }
}
class B extends A {
#x = 200;
getB() { return this.#x }
}
const b = new B();
b.getA() // 100
b.getB() // 200;Intuitively, you would expect the subclass to essentially be saying "my version of this variable is equal to 200, not 100". Then both calls should return the same result (200)
I think the feature is good but couldn't they find a different character than one used for comments almost everywhere else?
But it is an object property, just like everything else attached to "this".
That "this.foo" is not "this.#foo" that's because "foo" is a different key than "#foo".
The current idea is that private fields are kinda like this:
export class Bar {
#foo = 100;
setFoo(value) {
this.#foo = value;
}
getFoo() {
return this.#foo;
}
getFooFromOtherBar(otherBar) {
return otherBar.#foo;
}
}
// similar to (but implemented differently I'm sure)
const barPrivate = new WeakMap(); // properties are only private if this is never exported
export class BarWithoutPrivateFields {
constructor() {
barPrivate.set(this, {});
barPrivate.get(this).foo = 100;
}
setFoo(value) {
barPrivate.get(this).foo = value;
}
getFoo() {
return barPrivate.get(this).foo;
}
getFooFromOtherBar(otherBar) {
return barPrivate.get(otherBar).foo;
}
}
It is NOT a property key! this["#foo"] = "Value A"
this.#foo = "Value B"
console.log(this["#foo"]) //=> "Value A"
console.log(this.#foo) //=> "Value B"
That's why people are saying that `this.#foo` is not an object property. You can think of it like one if you want, but it's not really the same thing.I know this is very non-constructive but reading through these threads and these proposed improvements to modern JS the expression "polishing a turd" pops up in my mind repeatedly. That people chose to bring this thing out of the browser to use as a general purpose language is rather baffling to me. I can't wait for WebAssembly to become more mainstream, then I'll be able to archive my little knowledge of JS deep into my brain, alongside Perl and TCL.
</nonconstructive rant>
You can't throw an error on `this["#foo"]` because then you'd be publicly exposing the fact that a private field `this.#foo` exists.
But just “knowing they exist” is not causing any pain for a library developer, as long as they can’t read the value. So I don’t understand the need to also hide the fact they exist. What’s the use case there?
It seems you get all the benefit with almost no downside by just making them non-read/write but by going fully to non-enumerable you not introduce two massive downsides:
1. Introducing a whole new syntax that has multiple irregularities (property access) and uses a special character.
2. Now can confusingly have the same public and private names on the same class.
1. Introducing a whole new syntax that has multiple irregularities (property access) and uses a special character.
I sincerely fail to see how this has irregularities. It's straightforward. x.#prop is always a private field access and x.prop is always x["prop"] which is always an object property access. 2. Now can confusingly have the same public and private names on the same class.
I know what you're saying, but the reason it is confusing is because it is incorrect. You cannot have have any same public and private names because private names must start with # and public names cannot start with #. Private field names exist only in a class-definition-bound namespace.I know you can't think of a use case. But the committee of TC39 (language maintainers, popular library creators and maintainers) have thought of the use cases and have clearly decided it is useful.
There has been a lot of discussion regarding public and private keywords here: https://github.com/tc39/proposal-class-fields/blob/master/PR...
JavaScript is not static, you don't have a compiler that can protect against access, so you need info that is preserved at runtime.
`#count` in this case is a different name than `count` and via this naming convention it's able to protect access. I don't get the point though, since people already use underscores as a soft mechanism for protecting access to private stuff and in a dynamic language like JS I don't think you need more.
I also think the TC39 proposals are doing more harm then good and they should just stop changing JavaScript. Just leave it the way it is, thanks.
"Promise" for example sucks, in spite of receiving early feedback, because TC39 is run by certain individuals that outright reject feedback that doesn't comply to their world view.
Is there? The object.#foo syntax tells you it is private, sure… but you still need know if access is permitted or not. For that, you need to look at some information on object and make a decision, but that information could also inform you as to whether or not the access is to a private field at all. Consider for object.foo, the following:
1. look up, internally in the interpreter, the class definition (recorded when parsing the class {} block)
2. Is .foo private? If no, behave "normally"
3. If yes, decide if access should be allowed.
What about that doesn't work? If that does work, then, the # is pure syntax.One thing I'm curious about, since you seem adamant that this isn't syntax (particularly since the docs raise the peculiar "SyntaxError" — which implies determining at parse time whether a give obj.#thing is valid): is it only accessible through this.#foo? That is, if I have a class with a member function that takes another instance of the same class, is JavaScript going to tell me that I can't access private fields, despite those being on the current class? (This is fairly different from most languages, and makes some things impossible to express while keeping internals private.)
The reason I said that the difference between `object.foo` and `object.#foo` is not purely syntax is because they are two completely different actions. The first is a lookup in the object's property map/prototype chain. The second is a private field lookup.
`object.foo`:
1. Check to see if there's a property named "foo" in `object`'s property map.
1a. If yes, return the value.
1b. If no, continue.
2. Does this object have a prototype?
2a. If no, return undefined.
2b. If yes, repeat these steps starting from 1 using this object's prototype.
`object.#foo`: 1. Test to see if you're in a class.
1a. If no, throw a Syntax Error.
1b. If yes, continue.
2. Test to see if `object` is an instance of this class.
2a. If no, throw a Syntax Error.
2b. If yes, continue.
3. Test to see if the class defines a private field *#foo*.
3a. If no, throw a Syntax Error ? (Not positive on this one, it might just return undefined).
3b. If yes, continue.
4. Look up this class' private fields for `object`.
5. Get the value for *#foo*.
In case it wasn't clear: the private field `object.#foo` is NOT a property on `object` named `foo` and marked private. It is a field which exists in a namespace only accessible to the class definition. Without the # sigil, there'd be two issues. See [0] for a pseudo example of how private fields really behave.The first issue comes from a decision made in the spec by the committee: private fields must not interfere with object properties. This essentially means that an object can have the private field #foo AND the object property "foo" (as I described here [1]). Without the #, the fields would have to exist in the same namespace as the object's properties. If you error out when `object.foo` is accessed outside of the class definition, it would not fit the spec's definition of "private". [2] [3]
The second issue is that without the #, the compiler would need to determine at the call site which lookup method to use, and they decided this would incur too much overhead on every single lookup in the already complicated property lookup logic.
0: https://news.ycombinator.com/item?id=18707421 1: https://news.ycombinator.com/item?id=18706363 2: https://github.com/tc39/proposal-class-fields/blob/master/PR... 3: https://github.com/tc39/proposal-class-fields/blob/master/PR...
Why couldn't it? E.g why not let it interfere...
Please roll this back, what a nightmare!
Many system programmers will think this line is commented out. Indeed what a nightmare!
The muscle memory is so strong, when writing Javascript/Typescript I type # by accident sometimes.
But then, // is one of the most commonly used operators in another language I use, so it's weirdly symmetrical :)
Like ||, but // tests definedness instead of truthiness.
The equivalent in several other languages is ??, which is uglier imho, but it's just syntax. https://en.wikipedia.org/wiki/Null_coalescing_operator
I use // and ?? often as it's conceptually clean, fast and concise. When available, it arises naturally in logic like caches, memoizations and defaults. //= is pretty useful too, and I'm surprised it's not more widely available.
That abomination requires as much rationale as they can produce in the given time frame.
Good design doesn't require such degree of mental gymnastics.
Is this problem found anywhere else in the programming world at this scale?
Looks like Typescript conventions say not to do that. Either way the compiler should throw an error if trying to access a private incorrectly.
In JS I guess it helps reduce runtime errors since there is no compiler to check for you and again very clearly visible in code its a private.
Its sort of old school, like Hungarian notation where you would prefix variable with type identifiers except they really mean something.
I cringe every time I have to deal with such codebases.
IMO it was a mistake for C++ to allow the "this" to be implied for members, it makes it a lot harder to figure out what C++ code is doing and what's dealing with member variables and methods vs. globals. At the same time "m_" prefix only solves half of the problem because it doesn't help with method calls. That's why I prefer to drop the 'm_' and just put the "this->" everywhere, even when it's not mandatory.
The m_ syntax is kind of ugly but adding a this-> every time seems foolish, given that either the member is protected or public if you're modifying it in a base class. In the case of complex inheritance, you might be running into problems with ambiguity of names anyway.
m_variable (or a sensible member naming convention, e.g. a prefix) helps to distinguish between local variables, which is important in complicated classes/functions.
This proposed # for JavaScript looks insane, coming from the well-established, tried, and tested (and perfectly functional and useful) systems found in C++, Java and C#. It's like it's adopting a terrible syntax in order to solve a problem that doesn't exist. Perhaps I am misunderstanding.
If the point is to write super dense code for some reason then don't use anything at all.
I for one am more than happy not to write Hungarian notation any longer.
Even Microsoft style manuals now advise against it for modern Windows code.
Language design is hard but is there legitimate criticism besides subjective "I don't like this" ...
I wouldn't be surprised if this here is them giving that dominance a first shot. I'm sure they realize that developers will think it's horrible, but at the same time it's also not a problem. Nothing will break because of this, no company will be disadvantaged by it (barring Mozilla ofc). Perfect opportunity for testing the waters of a newly obtained monopoly position.
"V8 release v8"
At least when it's spelled out like this, they always use capital V8 for the engine and a lowercase v8 for the version.
I can imagine the verbal V8/v8 confusion probably forces everyone to say "version 8"
maybe
https://shop.gandi.net/en/domain/suggest?search=hackernews.d...
It makes it much more difficult to maintain downstream builds when the build-system breaks completely for indeterminate amounts of time.
You can check how the locales work here: https://github.com/unicode-cldr/cldr-misc-full/tree/master/m...
Go to the locale folder and then look at the listPatterns.json file
As noted by narrowtux, not all English locales use the Oxford comma; en-AU also doesn’t, which mollifies me.
The simplest style of example I can think of is nesting conjunctions: “the difference between A and B, and C and D”. (This example isn’t great because it depends on nesting and has only two items in each list; but it’s the first that springs to my mind. There are others that don’t require nesting or only two items, though normally not quite so clear-cut.) Removing the comma or shuffling things around in any way would completely change the meaning of the sentence. This is the sort of subtle thing that some will miss when writing, but in speech it’d be very clear.
https://v8.dev/blog/improved-code-caching
using this:
https://www.npmjs.com/package/v8-compile-cache
Or see the options with "cache" in their name, here: https://nodejs.org/api/vm.html#vm_new_vm_script_code_options
I thought they have like 10.000 websites they measure
> We now monitor changes against a test suite of approximately 25 > websites in order to guide V8 optimization. In addition to the > aforementioned websites and others from the Alexa Top 100, we selected > sites which were implemented using common frameworks (React, Polymer, > Angular, Ember, and more), sites from a variety of different geographic > locales, and sites or libraries whose development teams have > collaborated with us, such as Wikipedia, Reddit, Twitter, and webpack. > We believe these 25 sites are representative of the web at large and > that performance improvements to these sites will be directly reflected > in similar speedups for sites being written today by JavaScript > developers.
Obviously they would want to test Facebook because a lot of people use that website. I'm sure they tested many others as well.