In 2015, the ECMAScript language specification which is used as the standard for JavaScript in many browsers published its 6th version. This update is the successor to the version published in 2009 and a lot has changed in the JavaScript world since then.
One of our React apps at work was being updated to a new linter configuration to conform with the Airbnb ES6 standard and it gave me an opportunity to learn about many of the new feature and syntax improvements that have been introduced in this update.
I’m only going to go over a few of the most significant and interesting changes that we have had to make in our updated applications but the entire specification is available here.
ES6 introduced a new function shorthand syntax and it has quickly become one of my favorite new features of the language. Arrow functions replace the classic function() {}
, making code much less verbose.
In addition to cleaning up the function syntax, the power of the arrow function comes from its implicit binding of the function’s this
keyword to its value in the enclosing scope. In the following example, a React class needs to render the HTML for each item in its passed down properties:
The items mapping has to bind this
in order for the call-site of renderItem
to have to the function. With arrow functions, the mapping can be expressed more simply as shown below:
Other use cases for arrow functions include callbacks to asynchronous functions such as with AJAX requests or timeouts.
One of the most interesting changes in ES6 is the introduction of classes. Object-oriented classes were previously achievable in JS using prototype-based OO patterns, but the introduction of the official class syntax provides a consolidated, clean class interface. React has embraced the use of ES6 classes and now encourages the use of component extension over the traditional React.createClass
.
The example below illustrates the new class-based React component syntax:
At this point you may have some questions such as the following:
Where did getInitialState
go? What does super do? Wait why are there backticks and what is even happening with that first line in the render method?
All in good time. The above ES6 class is a lot to process when coming from an ES5 world so let’s break it down into parts:
The extends
keyword is used to create a class that inherits from the extended class.
On instantiation, the constructor method is called automatically and it can call the extended class’s constructor using the super
keyword. In React, the parent class must call super
in order for this
to be available in the constructor.
The initial state for the React class is set in the constructor, replacing the getInitialState
method. The getDefaultProps
method has been similarly replaced by the defaultProps
property.
Backticks are used to support ES6 template strings, which are similar to the string interpolation mechanisms in other languages like Python or Ruby. Gone are the days of concatenating strings using +
and it is a welcome change.
The render method is using two small but important new features from ES6. The first is the const
keyword for variables and must be initialized with a value. If you try and change the value of a const variable after its declaration it will immediately throw a TypeError
. The mutable version of this keyword is let
and both the new variable types are block-scoped in order to address unwanted variable hoisting and scope congestion such as in the situation below:
The programmer’s intention is often to use variable x
only in the context of the conditional statement, however, variable hoisting declares x
in the scope of the function, making it undefined when it is printed rather than throwing a ReferenceError like y
.
Replacing var
with let
or const
would cause x
to also throw a reference error and helps the developer determine what went wrong.
The second feature introduced in the render method of our ES6 React component is called destructuring. Destructuring uses pattern matching and in the simplest case like the one above, it creates a const
local variable called name equal to the value of the name property from the state object. Naming local variables differently from the properties of the object can similarly be achieved in our example using more complex pattern matching: const { newName: name } = this.state
. Destructuring can be nested arbitrarily and is a powerful new feature of the language.
The first line of our new React component imported React
and Component
from the React library using new language-level support for modules in JavaScript with influences from the popular CommonJS and AMD formats. Building imports into the language provides developers with a consistent and optimized module syntax that can be statically analyzed.
The following example shows the basic use of this import/export functionality:
If we wanted to import all of the exports from the lib file we could use the syntax import * as lib from 'lib'
to name the aggregate exports under the lib
object.
In the case that we want our module to consist of only a single export, then an ES6 module can use the default
keyword to select a default export that can then be implicitly imported from other files:
After having migrated all of our components to the new ES6 syntax and used the new language features on a daily basis for the past month I have found the ES6 specification to be a welcome addition to JavaScript. Our conversion has resulted in clean, readable code that takes advantage of the powerful new features of ES6 and I’m looking forward to seeing how the language continues to evolve. For anyone who is interested, you can read up on the already drafted ECMAScript 2017 standard here.