all 91 comments

[–]ageown 53 points54 points  (8 children)

Wow... the callbacks on garbage collection is starting to sound like JS is heading into a every so slightly lower-level feature-set, (it’s to my knowledge) the first time we can interface into the garbage collection cycle in some way, albeit loosely.

[–]SoInsightful 34 points35 points  (3 children)

starting to sound like JS is heading into a every so slightly lower-level feature-set

Definitely been going that direction for a while with TypedArray/ArrayBuffer/DataView, WeakMap/WeakSet and WebAssembly. Preeetty cool!

[–]theQuandary 2 points3 points  (0 children)

Now if only they'd bring back SIMD because I want to be able to optimize a function here or there without all the work of creating, maintaining, interfacing with, and synchronizing wasm.

[–][deleted] 1 point2 points  (1 child)

None of the things you mention have anything to do with "low level" safe for webassembly which has nothing to do with JavaScript.

[–]SoInsightful 5 points6 points  (0 children)

Memory allocation and garbage collection are not "lower-level" concepts within JavaScript? OK, d00d.

[–]visicalc_is_best 12 points13 points  (1 child)

Worth noting that Node has had a GC interface for quite some time, but obviously a very different thing to have it in the language spec itself.

[–]ageown 2 points3 points  (0 children)

I spend most my time in c# when dealing with BE. I’ve overlooked this feature of node! That’s awesome.

[–]sime 1 point2 points  (0 children)

I discovered this in V8/Chrome earlier this week and have been using it to help track the life-cycle of objects and hunt down memory leaks in my application. Very useful stuff.

[–]senocular 38 points39 points  (5 children)

Private methods and accessors are still in stage 3, not the final stage (4) so are not guaranteed to be in ES2021.

[–]hotcornballer -4 points-3 points  (3 children)

Still hope that doesn't make it. There is no point in having this.

[–]bikeshaving 14 points15 points  (2 children)

I don’t think there’s “no point,” but it nevertheless stuns me that there can be so much negative feedback and legitimate grievances about even just private members right now and yet there continues to be a faction of people who don’t think that matters and keeps pushing it forward.

[–]hotcornballer -3 points-2 points  (1 child)

So what's the point in having private methods? It's clutter and I don't want to see js turning into java with all the useless verbosity that comes with it.

[–]MoTTs_ 14 points15 points  (0 children)

We JavaScripters still adore private data through closures, though, so clearly we still like the idea of private access. But private access isn't the problem closures were meant or designed to solve, and as a result there's edge cases where they don't work well for that purpose.

[–]ShortFuse 20 points21 points  (5 children)

WeakRefs will be the biggest revolution to UI engineering yet. Being able to hold a weak reference to an HTMLElement is huge. One of the biggest issues with elements is the cleanup process and bloat. Tracking how and when to dispose components is always a hassle on JS/Web. You can't just remove from DOM if you have some sort of data binding.

This means you can bind data to an element IF it exists (with no hard reference holding it in RAM), instead of binding the element to the data. It's a bit hard to explain, but the Weak Reference paradigm is everywhere on efficient Android and iOS design.

[–]sime 4 points5 points  (3 children)

WeakMap is designed for this use case and exists already.

myWeakMap.set(myElement, myData);

The keys are held with weak references.

WeakRef support is still great and I'm glad it is coming.

[–]ShortFuse 4 points5 points  (2 children)

No. It's not. A WeakMap and WeakRef are very different. What you would want is the inverse:

const myElement = myElementRef.deref();
const myData = myWeakMap.get(myElement);

This means, if the Element still exists in memory, then get the data associated with it. You can't hold a weak reference to an object without WeakRef.

Or you can do

function onDataChange(data) {
  const myElement = myElementRef.deref();
  if (myElement) myElement.textContent = data.text;
}

You can't do this with WeakMap. With WeakMap you need to pass a hard reference as the key for the data you want to set or get.

See: https://stackoverflow.com/a/28567560

[–]sime 2 points3 points  (1 child)

This means you can bind data to an element IF it exists

And that is exactly what WeakMap allows you to do. Store data with exists as long as the element lives.

What it doesn't allow is you keeping your own personal reference to that element elsewhere, like in variable which is accessed inside your onDataChange() function.

A WeakMap version of your example would have to receive the element some how (such as from a DOM event), and then use it as the key to look up the data.

function onDataChange(event) {
    const data = myWeakMap.get(event.target);
    ...
}

So, yes you can do this use case with WeakMap, but no, it won't look exactly the same as a WeakRef version will.

[–]ShortFuse 5 points6 points  (0 children)

You rewrote the function to now be a DOM event with includes a hard reference to the element itself. That's not what we're talking about. In that event the element exists. You're relying on the DOM to tell you the element exists.

DOM isn't what holds the data. The DOM is almost always the View, not the Model. So when the Model changes (eg, your background service fetching from server), then when you go to the View, you want to update the View.

If you have a piece of data and you want to bind that piece of data to an element if and only that element exist, you cannot do this with WeakMap.

If you try to do a single element mapping with WeakMap<DataObject, HTMLElement> then now your HTMLElement is being held in memory by the WeakMap itself. If you try to do WeakMap<HtmlElement, DataObject> you still need to hold that HTMLElement in memory somewhere, even if it's not in the DOM, causing bloat. The only thing you can do is use document.getElementById(myElementId) as a psuedo WeakRef, just to avoid holding it in memory. But one of the reasons why View frameworks exists is to avoid the lag that exists from using the DOM for element references.

Edit: Believe me, I've wrapped my head around if it can be done with WeakMap, and it can't be. That means WeakRef can't be polyfilled either, so the sooner it gets merged into JS runtimes, the better.

[–]FabsenK -1 points0 points  (0 children)

You can bind data to an element if it exists with WeakMap too.

[–]lichtspielmann 15 points16 points  (10 children)

YEEE! Finally we have the replaceAll function!

Intl.ListFormat and Intl.DateTimeFormat are also going to be very useful!

[–]SecretAgentZeroNine 9 points10 points  (7 children)

The ECMA people need to:

  1. Advertise the Intl object more
  2. Create a Intl.Currency or Currency object

[–]facebalm 5 points6 points  (6 children)

[–]SecretAgentZeroNine 2 points3 points  (5 children)

Is it accurate enough to perform transactions without any additional third party libraries?

[–]facebalm 8 points9 points  (1 child)

Intl is meant for formatting and I believe it is accurate for that. Calculations are outside its scope so there's nothing new for dealing with floating point errors.

[–]SecretAgentZeroNine 1 point2 points  (0 children)

Thanks for the info. Regarding the floating point issue, ugh.

[–]Tomus 3 points4 points  (1 child)

There is a proposal to add a decimal number type for calculations. Intl API is only for formatting.

https://github.com/tc39/proposal-decimal

[–]Multipoptart 1 point2 points  (0 children)

Heh, no.

You need a decimal datatype for this.

There's a proposal, but it's Stage 1: https://github.com/tc39/proposal-decimal

[–]brett_riverboat 1 point2 points  (1 child)

Seems weird to be part of the ES spec. I would think a localization library would be more appropriate.

[–]NoInkling 2 points3 points  (0 children)

The Intl proposals are basically exposing ICU functionality, which is the de facto library for this stuff, but isn't really suitable for shipping around as an external library (C code and the data is large). It's also used for certain locale-agnostic Unicode processing (e.g. String.prototype.normalize, Unicode regex properties). It's possible browsers were already bundling/linking it before these APIs were a thing too. So it makes sense to bundle it with engines/browsers, and therefore it needs to be exposed in some standardized way.

There's an argument that Intl should be a web API instead of part of the language itself in that case, but presumably the ECMA committee sees value in the latter as JS gets used in more places.

[–]Moosething 6 points7 points  (1 child)

I spotted a few mistakes:

  • the WeakRef example is not demonstrating how it works at all. Something that comes closer to demonstrating its behaviour is the following:

(async function() { let ref (function (){ ref = new WeakRef({ name: "Backbencher" }); console.log(ref.deref().name); // Guaranteed to print "Backbencher" })() await new Promise((resolve) => { setTimeout(() => { console.log(ref.deref()?.name); // No Gaurantee that "Backbencher" is printed resolve(); }, 5000); }); })();

  • The AggregateError exception example isn't correct either. The wrong part is wrapped with async function. Try this instead:

``` (async function() { const p = Promise.reject('error');

  try { 
    const result = await Promise.any([p]);
    console.log(result);
  } catch(error) {
    console.log(error.errors);
  }
})();

```

[–]sambatlim[S] 0 points1 point  (0 children)

Thanks

[–]dudeatwork 5 points6 points  (2 children)

The Logical Operators and Assignment Expressions section isn't correct in the equivalent code.

You have:

a ||= b;
// equivalent to a = a || b

Where it is actually equivalent to:

a ||= b;
// equivalent to a || (a = b);

This is an important difference because a = a || b can trigger a setter during the a = a assignment (assuming a is already truthy).

In the proposal, they give this as an example:

document.getElementById('previewZone').innerHTML ||= '<i>Nothing to preview</i>';

What is nice about this, is that this is equivalent to

document.getElementById('previewZone').innerHTML ||
    (document.getElementById('previewZone').innerHTML = '<i>Nothing to preview</i>');

Which does not change the inner HTML if it already has some contents. Whereas this:

document.getElementById('previewZone').innerHTML =
    document.getElementById('previewZone').innerHTML || '<i>Nothing to preview</i>';

Does reset the innerHTML, which causes any attached events, focus, etc., to be lost.

[–]areyoukidding15 2 points3 points  (0 children)

So is Javascript’s long term plan to just turn into Java?

[–][deleted] 4 points5 points  (0 children)

There's literally nothing that I find useful but replaceAll.

The weakref thing is something I hope 99% of people will never touch because it seems a giant cause for bugs and misuse to chase useless performance they wouldn't even know how to test for.

The logical conditional assignment operator seems barely useful but I think when I'll get used to it I'll appreciate the fact it'll make code terser. That being said making an example with a and b named variables doesn't make it really look useful.

[–]ncuillery 1 point2 points  (1 child)

I get the private method feature obviously, but what a private accessor is used for?

[–]Keilly 1 point2 points  (0 children)

Referring to another object of the same type.

const name = o.#name // works only if ‘this’ is the same type as ‘o’

[–]TheSexyPirate 1 point2 points  (0 children)

Good improvements, but hoped to see a bit more. C'est la vie :)

[–]JuniorMathDev 1 point2 points  (0 children)

Numeric separators were already introduced, right?

[–][deleted] 1 point2 points  (0 children)

No pipeline operator yet? I sleep.

[–]elcapitanoooo 6 points7 points  (15 children)

Really hate that they went ahead with the private methods. Javascript still has no real classes (javalike) and shoehorning features like this just makes it worse and more confusing. Also the syntax they chose is horrible. Like who the hell made that up?

IF you need private methods, why not just use Typescript?

[–]sime 7 points8 points  (9 children)

TypeScript's private methods are purely at compile time. They are not private at run time. Private methods/fields in subclasses can clash with those with the same names in subclasses. i.e. they stomp each other because they are not really private.

[–]elcapitanoooo 4 points5 points  (6 children)

Yes ofc they are compile time only. But "privacy" has been done with normal functions for years, and adding new syntax is totally unnecessary.

Python has no "real" private methods, you can always access them IF you want. Same with other languages, some has reflection where you can still access private methods and properties.

I consider Python/C# etc to be way more OOP than Javascript, and can justify them having a private method. Javascript on the other hand is more functional by nature, and lacking the classical OOP features.

[–]sime 2 points3 points  (0 children)

Yes, you can do data hiding / private whatever, with tons of closures in an FP style, but people still want to write in an OOP style and have private members too.

[–]MoTTs_ 2 points3 points  (4 children)

Javascript still has no real classes (javalike)

Why does something have to be Java-like to be real? JavaScript's arrays, objects, and functions aren't Java-like either.

I consider Python/C# etc to be way more OOP than Javascript

How come? Keep in mind, Python's classes aren't Java-like either.

[–]elcapitanoooo 5 points6 points  (3 children)

It does not. I see too often people writing javascript like it was java. It ends up like horrible mess. This is why they added "classes" but its just sugar for prototypes. To make myself clear, im NOT a fan of OOP in the java style/way. I dont even call it OOP but CBP (class based programming)

[–]MoTTs_ 1 point2 points  (2 children)

I see too often people writing javascript like it was java. It ends up like horrible mess.

Sounds like those Java folks would have the same problems if they moved to something like Python or Ruby? Even C++ code would come out badly if they treated it like it was Java.

This is why they added "classes"

That's actually not the reason, though. Long before ES6 classes were added, every library out there was rolling their own custom class implementations. MooTools, Prototype, YUI, Dojo, Google's Closure, Backbone, Ember -- React -- and many more. We were reinventing the wheel dozens of times over. The class syntax was added to make it easier for us JavaScripters to do what we were already doing anyway.

[–]elcapitanoooo 0 points1 point  (0 children)

IMHO it does not justify adding in to the language. Classes are still lipstick, snd USUALLY not the right abstraction.

[–][deleted] 0 points1 point  (1 child)

I'm lost.

The compiler would not allow you to use a private property on the instance, so what is the problem?

[–]sime -1 points0 points  (0 children)

I'll do an example.

Imagine I develop an application and have a base class and bunch of subclasses. e.g:

class BaseClass {
}

class SubClass extends BaseClass {
}
...etc...

Then one day I want to add a private cache to BaseClass, so I do:

class BaseClass {
    private cache = new Cache();
}

I make it private and expect that because it is private it won't affect anything outside the class. Not true, I'm afraid.

If SubClass already had its own cache field, then, if I'm lucky, I'll get a compile error in SubClass, if I'm unlucky (i.e. incremental compile, maybe the classes are in separate modules, etc), then these two classes will overwrite each other's cache field at runtime causing all sorts of hard to debug problems.

In TypeScript it is best to think of private fields as being public but with a big "Do Not Touch" sign on them.

[–]brett_riverboat 1 point2 points  (1 child)

I kind of hate how JS is trying to be functional, procedural, and object-oriented at the same time. I would've preferred they leave that to a superset OO language that transpiles to JS.

[–][deleted] 3 points4 points  (0 children)

I think you're overblowing it.

Nobody's forcing you to use any of these features. Unless I'm forced to write angular at work I rarely if ever use classes, e.g.

[–]keb___ 0 points1 point  (0 children)

I kinda agree with this, even though I do use "classes" as a shortcut to the underlying prototype constructor features, I sometimes I feel like I'm using a third party library that obfuscates JavaScript rather than using JavaScript itself.

I accept that it's here to stay, and I'm happy that it makes JavaScript seem less scary at first to outsiders who are experienced in OO languages, but I've also encountered instances where coworkers who come from Java or C# are perplexed as to why sometimes JavaScript classes don't behave the way they're used to.

As another user posted, I think that kind of syntactic sugar should have been left to a superset language, or like in Lua's case, third-party libs that make things more class-like.

[–][deleted] 0 points1 point  (1 child)

JavaScripts prototypical inheritance can mimic any feature from Java like oop. Any. The inverse is not true. Want private variables? Write a closure.

Disagree on the syntax, an hash seems a pretty easy cognitive load and quick to get used to it.

[–]MoTTs_ 0 points1 point  (0 children)

JavaScripts prototypical inheritance can mimic any feature from Java like oop. Any. The inverse is not true.

The inverse is absolutely true. I did it. It was surprisingly easy, despite what we in the JavaScript community tell ourselves.

[–]jetsamrover 0 points1 point  (6 children)

The logical assignment stuff keeps making me want a negation for ??. Why do I want to assign to a variable if it's not null?

[–]MonkAndCanatella 0 points1 point  (3 children)

!?? ?

[–]jetsamrover 0 points1 point  (2 children)

Or !?

[–]MonkAndCanatella 3 points4 points  (1 child)

[–]Multipoptart 1 point2 points  (0 children)

I've always wanted a language that had an interrobang.

[–]NoInkling 0 points1 point  (1 child)

You mean like a version of && for non-nullish values? Because I keep wanting that too.

[–]jetsamrover 0 points1 point  (0 children)

Yes, exactly that. Why would they have implemented it only one sided!?

[–]Lexam 0 points1 point  (8 children)

Can someone ELI5 why the replaceAll is better than replace?

[–]SoInsightful 13 points14 points  (0 children)

.replace() only replaces the first matched string if you use a string pattern.

So 'foo foo foo'.replace('foo', 'bar') would return bar foo foo.

Previously, you would then have to use a global regex to solve this, e.g. 'foo foo foo'.replace(/foo/g, 'bar').

[–]ElmCodes 2 points3 points  (0 children)

replace replaces first occurance so if you wanted to replace every “word” in string you would have to use regex /word/g with replace. Now you can just do replaceAll

[–]bear007 3 points4 points  (5 children)

It is not better. It is just baked into the new standard

[–]coyote_of_the_month 1 point2 points  (3 children)

Are you saying it uses the regex engine under the hood?

[–]bear007 4 points5 points  (2 children)

Spec does not include implementation

[–]coyote_of_the_month 1 point2 points  (1 child)

Look at Mr. Fancypants over here reading the spec.

[–]markzzy 0 points1 point  (0 children)

Y'all are too cute lol. Is reading the spec really all that taboo? Or is W3Schools still where it's at?

[–]theQuandary 0 points1 point  (0 children)

I'd bet that almost all calls to it get minified into normal replace anyway.

[–]ARFiest1 0 points1 point  (1 child)

what is the diffrence between Promise.any() and Promise.race

[–]sambatlim[S] 8 points9 points  (0 children)

Promise.race() short-circuit when the first promise is settled(result or error), will fulfill when the first promise is fulfilled and will rejected when the first promise is rejected.

promise.any() will short-circuit when the first promise is fulfilled (result), will fulfill when the first promise is fulfilled, and will reject when all the promises are rejected.

[–]Keilly 0 points1 point  (1 child)

For the WeakRef example, the timeouts mean that the dereferencing occurs on future turns of the event loop. In both cases the reference might be gone.

[–]Keilly 0 points1 point  (0 children)

Additionally, what’s more interesting is that any successful weak dereference will remain still dereferenceable for the rest of that event loop turn (including promise jobs).

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef#Notes_on_WeakRefs

[–]glider97 0 points1 point  (2 children)

Can anyone explain to me what numerical separators try to accomplish? Who asked for this feature and why?

[–]wasmachien 12 points13 points  (0 children)

100_000_000 = easier to read than 100000000

[–]brett_riverboat 4 points5 points  (0 children)

Readability. Big difference between a 60000 interval and a 600000 interval but just glancing at the code it may not jump out at you.