Because polyfills and transpilers have become popular in recent years, some early-stage proposals have gained significant adoption before they’ve even been finalized. And, because proposals can change widely before being accepted, some might find that they’re using a feature that will never be part of the language.
Before getting into the proposals that I think are most interesting, let’s take a second to familiarize ourselves with the current process.
Stage 0 “strawman” — The starting point for all proposals. These can change significantly before advancing to the next stage. There is no acceptance criteria and anyone can make a new proposal for this stage. There doesn’t need to be any implementation and the spec isn’t held to any standard. This stage is intended to start a discussion about the feature. There are currently over twenty stage 0 proposals.
Stage 1 “proposal” — An actual formal proposal. These require a “champion”(i.e. a member of TC39 committee). At this stage the API should be well thought out and any potential implementation challenges should be outlined. At this stage, a polyfill is developed and demos produced. Major changes might happen after this stage, so use with caution. Proposals at this stage include the long-awaited Observables type and the Promise.try function.
Stage 2 “draft” — At this stage the syntax is precisely described using the formal TC39 spec language. Minor editorial changes might still happen after this stage, but the specification should be complete enough not to need major revisions. If a proposal makes it this far, it’s a good bet that the committee expects the feature to be included eventually.
Since there are so many proposals, here are a few of the more interesting ones currently under consideration.
ECMAScript 2015 added iterators, including the for-of loop syntax. This made it considerably easy to loop over iterable objects and made it possible to implement your own iterable data structures.
Unfortunately, iterators can’t be used to represent asynchronous data structures like accessing the file system. While you could always run Promise.all and loop over an array of promises, that requires synchronous determination of the “done” state.
For example, instead of reading all the lines from a file before working with them, with async iterators you can simply loop over an async iterable that reads the lines as you need them.
You can define a async generator function by simply using both the generator syntax and the async-await syntax together:
You can then use this async generator in a for-await-of loop:
Any object that has a Symbol.asyncIterator property is defined as being async iterable and can be used with the new for-await-of syntax. Here’s an example of this in action:
The proposal is currently at stage 3, and browsers are starting to implement it. At this stage it’s likely going to be included in the standard and eventually implemented by major browsers. However, there may be minor changes to the spec might happen before that so using async iterators today carries some degree of risk.
The regenerator project currently has basic support for this async iterator proposal. However, regenerator alone does not support the for-await-of loop syntax. The Babel compiler has the transform-async-generator-functions plugin which supports both async generator functions and the for-await-of loop syntax.
There is a proposal to add public and private fields and private methods to the class syntax that was introduced in ECMAScript 2015. This proposal is the culmination of a long period of discussion and competition between various competing proposals.
Using private fields and methods is similar to their public counterparts, but with their names prepended with a hash symbol. Any method or field marked private will not be visible outside of the class, ensuring strong encapsulation of internal class members.
Here’s an example of hypothetical of a React-like component using public and private fields with a private method:
The private class fields and methods are not currently polyfilled by Babel, although it will be soon. Public fields are supported by Babel’s transform-class-properties plugin, however this is based on an older proposal that was merged into this unified public / private class fields proposal. This proposal reached Stage 3 on September 1, 2017, so it will be safe to use any polyfill when they become available.
Decorators are a good example of a proposal that has changed completely after being introduced. Babel v5 implemented the original stage 2 decorators spec which defined a decorator as a function that accepts a target, name, and property descriptor. Today the most popular way to transpile decorators is Babel’s transform-legacy-decorators plugin, which implements this old spec.
The new spec is quite different. Instead of a function with three properties, we now have a formalized description of a class member — decorators being functions which mutate that descriptor. This new “member descriptor” is quite similar to the property descriptor interface introduced in ES5.
There now two different kinds of decorators with different APIs: member decorators and class decorators.
In the spec, “extras” refers to an optional array of member descriptors that can be added by a decorator. This would allow a decorator to create new properties and methods dynamically. For example, you might have a decorator create getter and setter functions for a property.
Like the old spec, the new one allows you to mutate property descriptors of class members. Additionally, decorators are still allowed on properties on object literals.
It’s likely that the spec will change significantly before it’s finalized. There are ambiguities in the syntax and many of the pain points of the old spec have not been addressed. Decorators are a huge syntax extension to the language, so this delay is to be expected. Unfortunately, if the new proposal is accepted you will have to significantly refactor your decorator functions to work with the new interface.
ECMAScript 6th edition added the import statement and finalized the semantics around the new module system. Major browsers just recently released support, although there are minor differences in how much of the spec they have implemented. NodeJS has released preliminary support for the ECMAScript modules in version 8.5.0 behind the — experimental-modules flag.
However, the proposal was missing an asynchronous way to import modules, makes it difficult to dynamically import module code at runtime. Right now the only way to dynamically load modules in the browser is to dynamically insert a script tag of type “module” with the import declaration as its textContent.
A build-in way of doing this is the proposed dynamic import syntax, which calls for a “function-like” import module loading form. This new dynamic import syntax would be allowed in module code as well as normal script code, offering a convenient entry point for module code.
Last year there was a proposal to solve this problem by proposing a System.import() function, but that idea was eventually left out from the final specification. This new proposal is currently at stage 3, and will likely be included by the end of the year.
Observables are created via the Observable constructor, which takes a subscriber function:
Use the subscribe function to subscribe to an observable:
The subscribe() function returns a subscription object. This object has a unsubscribe method that can be used to cancel the subscription.
Observables shouldn’t be confused with the deprecated Object.observe function, which was a way to observe changes to an object. That method was replaced with a more generic Proxy implementation in ECMAScript 2015.
Observables are currently stage 1, but it will likely advance to the next stage soon since it has been marked as “ready to advance” by the TC39 committee and there is strong support from browser vendors. There are already three polyfill implementations to choose from, so you can start using it today.
Do-expressions are a proposed new syntax for wrapping multiple statements in a single expression. This would allow you to do the following:
The last statement in a do expression is implicitly returned as the completion value.
Do expressions would be quite useful inside JSX. Instead of using complicated ternary operators, a do expression would make control flow inside JSX more readable.
Babel has a plugin to transform do-expressions. The proposal is currently at stage one and there are significant open questions around how do-expressions work with generators and async functions, so the spec might change significantly.
Another proposal inspired by CoffeeScript is the optional chaining proposal which adds an easy way to access object properties that could be undefined or null without lengthy ternary operators. This is similar to CoffeeScript’s existential operator but with a few notable features missing such as scope checking and optional assignment.
This proposal is at stage 1 and there is a Babel plugin called babel-plugin-transform-optional-chaining which implements the proposal. The TC39 committee had concerns about the syntax in their last meeting in October 2017, but it seems likely that an optional chaining proposal will be adopted eventually.
There is a proposal to standardize a global object that would be available across all engines and runtime environments. It’s currently a stage 3 proposal and will therefore be accepted soon.
There are over fifty active proposals under consideration right now by the TC39 committee, not including over twenty stage 0 proposals which haven’t advanced yet.
You can see a list of all active proposals on the TC39 committee’s GitHub page. You can find some of the more rough ideas on the stage 0 proposals section, which includes ideas like method parameter decorators and new pattern matching syntax.
There are also repositories of meeting notes and agendas from previous TC39 meetings where you can get an interesting look at the committee’s priorities and what problems are currently being addressed. If you are interested in Presentations, they are archived along with meeting nodes.
There has been several major syntax-changing proposals in recent editions of ECMAScript and this trend seems to be continuing. The pace of change is accelerating — every year there are more proposals and the 2018 edition looks like it’s going to have more accepted proposals than the 2017 edition.
This year there have been smaller proposals to add “quality of life” enhancements to the language like the proposal to add better type checking of build-in objects or the proposal adding degrees and radian helpers to the Math module. Proposals like these add to the standard library instead of changing the syntax. They are easier to polyfill and help reduce the need to install third-party libraries. Because they don’t change syntax, they are adopted quickly and often spend less time in the proposal process.
Install LogRocket via npm or script tag.
LogRocket.init() must be called client-side, not
Leptos is an amazing Rust web frontend framework that makes it easier to build scalable, performant apps with beautiful, declarative UIs.
We spoke with Dom about his approach to balancing innovation with handling tech debt and to learn how he stays current with technology.
Vite is a versatile, fast, lightweight build tool with an exceptional DX. Let’s explore when and why you should adopt Vite in your projects.