As programming languages go, JavaScript’s development has been positively frantic in the last few years. With each year now seeing a new release of the ECMAScript specification, it’s easy to get confused about JavaScript versioning, which version supports what, and how you can future-proof your code.

To better understand the how and why behind this seemingly constant stream of new features, let’s take a brief look at the history of the JavaScript and JavaScript versioning, and find out why the standardization process is so important.

The Early History of JavaScript Versioning

The prototype of JavaScript was written in just ten days in May 1995 by Brendan Eich. He was initially recruited to implement a Scheme runtime for Netscape Navigator, but the management team pushed for a C-style language that would complement the then recently released Java.

JavaScript made its debut in version 2 of Netscape Navigator in December 1995. The following year, Microsoft reverse-engineered JavaScript to create their own version, calling it JScript. JScript shipped with version 3 of the Internet Explorer browser, and was almost identical to JavaScript — even including all the same bugs and quirks — but it did have some extra Internet Explorer-only features.

The Birth of ECMAScript

The necessity of ensuring that JScript (and any other variants) remained compatible with JavaScript motivated Netscape and Sun Microsystems to standardize the language. They did this with the help of the European Computer Manufacturers Association, who would host the standard. The standardized language was called ECMAScript to avoid infringing on Sun’s Java trademark — a move that caused a fair deal of confusion. Eventually ECMAScript was used to refer to the specification, and JavaScript was (and still is) used to refer to the language itself.

The working group in charge of JavaScript versioning and maintaining ECMAScript is known as Technical Committee 39, or TC39. It’s made up of representatives from all the major browser vendors such as Apple, Google, Microsoft and Mozilla, as well as invited experts and delegates from other companies with an interest in the development of the Web. They have regular meetings to decide on how the language will develop.

When JavaScript was standardized by TC39 in 1997, the specification was known as ECMAScript version 1. Subsequent versions of ECMAScript were initially released on an annual basis, but ultimately became sporadic due to the lack of consensus and the unmanageably large feature set surrounding ECMAScript 4. This version was thus terminated and downsized into 3.1, but wasn’t finalized under that moniker, instead eventually evolving into ECMAScript 5. This was released in December 2009, 10 years after ECMAScript 3, and introduced a JSON serialization API, Function.prototype.bind, and strict mode, amongst other capabilities. A maintenance release to clarify some of the ambiguity of the latest iteration, 5.1, was released two years later.

Do you want to dive deeper into the history of JavaScript? Then check out chapter one of JavaScript: Novice to Ninja, 2nd Edition.

ECMAScript 2015 and the Resurgence of Yearly Releases

With the resolution of TC39’s disagreement resulting from ECMAScript 4, Brendan Eich stressed the need for nearer-term, smaller releases. The first of these new specifications was ES2015 (originally named ECMAScript 6, or ES6). This edition was a large but necessary foundation to support the future, annual JavaScript versioning. It includes many features that are well-loved by many developers today, such as:

ES2015 was the first offering to follow the TC39 process, a proposal-based model for discussing and adopting elements.

The TC39 Process

There are five stages through which a proposal must pass before it can be accepted into an upcoming version of ECMAScript.

Stage 0: Strawman

This is a convenience step to permit the submission of ideas to the specification. Features can be suggested by anyone — namely, TC39 members and non-members who have registered as a contributor.

Stage 1: Proposal

The first stage at which a proposal is formalized. It’s necessary that:

any existing problems rectified by the solution are described

an API outline is provided, alongside high-level implementation details, as well as polyfills and/or demos

potential impediments are discussed upfront.

A champion must be selected to adopt and advance the proposal. This individual must be a TC39 member.

Stage 2: Draft

This is the milestone at which a feature is likely to be included in a future version of ECMAScript. Here, the proposal’s syntax and semantics are detailed using the formal language described by the specification. An experimental implementation should be available at this point.

Here, the majority of the proposal and the backing technology have been developed, but it requires further feedback from users and implementers (such as browser vendors). Once this is available and acted upon, the outline and specification details are finalized and signed off by designated reviewers and the appointed editor. As a compliant implementation is required at this stage, only critical changes are henceforth embraced.

Stage 4: Finished

The proposal has been accepted and can be added to ECMAScript. It’s thus inherent that:

acceptance tests, which are part of the Test262 suite and are crafted with JavaScript, have been written to prove the conformity and behavior of the feature

at least two compliant implementations are available, and have shipped, all of which demonstrate robustness and developer usability

a pull request has been submitted to the official ECMA-262 repo, which has been signed off by the specification editor.

The above repository’s contribution document further details the use of GitHub issues and pull requests for managing additions to the language.

Moving Forward

Following the completion of ES2015 and the establishment of the TC39 process of JavaScript versioning and updating, subsequent releases have occurred each June, with the inclusion of proposals being timeboxed to one year. At the time of writing, there have been three new specifications.

ES2016

Also known as ES7, this was the first smaller, incremental version of ECMAScript. Aside from bug fixes, it added just two features.

This instance method simplifies searching for values in an Array :

const hasBob = names . indexOf ( 'bob' ) > - 1 ; const hasBob = names . includes ( 'bob' ) ;

Exponent Operator

Prior to ES2016, one could perform exponentiation with Math.pow(base, exponent) . This version introduces an operator (**) that has its own precedence:

Math . pow ( 5 , 3 ) ; 5 ** 3 ;

ES2017

A slightly larger release, ES2017 (aka ES8) contains a handful of useful methods and syntactical constructs.

Asynchronous Functions

Promises have saved us from callback hell, but their API nonetheless demonstrates verbosity. Asynchronous functions abstract them with a syntax that closely resembles synchronous code:

const getProfile = name => { return fetch ( ` https://some-api/people/ ${ name } ` ) . then ( res => res . json ( ) ) . then ( ( { profile } ) => profile ) ; } ; const getProfile = async name => { const res = await fetch ( ` https://some-api/people/ ${ name } ` ) ; const { profile } = await res . json ( ) ; return profile ; } ;

String Padding Methods

String.prototype.padStart(length, padder) and padEnd(length, padder) will respectively prepend and append padder (this is optional, defaulting to a space) to a string repeatedly until it reaches length characters:

'foo' . padStart ( 6 ) ; 'foo' . padEnd ( 6 ) ; 'foo' . padStart ( 10 , 'bar' ) ; 'foo' . padEnd ( 10 , 'bar' ) ;

Other features include trailing commas, shared memory and atomics, and static Object methods (Object.entries(), Object.values(), and Object.getOwnPropertyDescriptors().)

If you’d like to read more about the complete feature set of ES2017, please see our article covering what’s new in ES2017.

ES2018

This latest iteration, at the time of writing, introduces a small set of powerful additions.

Asynchronous Iterators

While Promise.all() allows you to await the resolution of multiple promises, there are cases in which you may need to sequentially iterate over asynchronously-retrieved values. It’s now possible to await async iterators along with arrays of promises:

( async ( ) => { const personRequests = [ 'bob' , 'sarah' , 'laura' ] . map ( n => fetch ( ` https://api/people/ ${ n } ` ) ) ; for await ( const response of personRequests ) { console . log ( await response . json ( ) ) ; } } ) ( ) ;

Object Spread and Rest Properties

Ostensibly, these two syntactical improvements are already popular amongst JavaScript developers thanks to the availability of compilers such as Babel. Object spread and rest properties are similar to array spread and rest properties, and permit the shallow copying and grouped destructuring of object properties:

const react = { name : 'React' , vendor : 'Facebook' , description : 'A JavaScript library for building user interfaces' , npm : true , cdn : true , } ; const vue = { ... react , vendor : 'Evan You' , description : 'A JavaScript framework for building UIs' , } ; const { name , vendor , ... rest } = vue ; console . log ( rest . description ) ;

Other accepted proposals are Promise.prototype.finally(), as well as enhancements to regular expressions and template literals.

If you’d like to read more about the complete feature set of ES2018, please see our article covering what’s new in ES2018.

A Final Word

JavaScript has evolved greatly over a short space of time. While this is attributable to the ECMAScript standard and the brilliant work of TC39, it was initially an arduous journey due to the previous lack of stability and cohesion in JavaScript versioning and development.

Thanks to the relatively mature proposals process, the language can only improve in a pragmatic and manageable manner. It’s a great time to be a web developer!