Learn about the new ES6 features, enhancements, and shortcuts introduced to the JavaScript programming language.
The sixth edition of ECMAScript– the scripting language specification by which JavaScript is standardized – introduced many new features, upgrades, and shortcuts to the JavaScript programming language. This version is commonly known as ECMAScript2015 or ES6.
When ES6 was first introduced, the features were not available on the vast majority of browsers, and it was necessary to use a compiler like Babel if you wanted to utilize any of the features. It is still the common convention to use a bundler like webpack to run Babel and compile ES6 down to code that older browsers can understand, but according to Can I Use, around 90% of all browsers can natively understand ES6 syntax.
It’s important to note that ES6 is not a different language, but simply the next version of ECMAScript, much like HTML5 is the next edition of HTML. You can still write all the same JavaScript code from the previous versions, but new syntax and features are now available.
In this article, we’ll go over some of the most commonly used features of ES6.
Let & Const
One of the most important new additions of ES6 are let
and const
statements.
Previously, there was only one way to declare a variable in JavaScript – the var
statement.
var color ='blue'
Now you can also assign values with let
and const
.
const user ='Nick'let active =true
There are a few important differences between the new keywords and the previously existing var
keyword.
Both let
and const
are block-scoped, meaning the variable can be scoped to any block, such as if
, for
, or while
. A var
is function-scoped, meaning only functions will introduce a new scope.
// Assign a variablevar color ='blue'let animal ='lion'if(true){ // Reassign variable in a block scope var color ='red' let animal ='tiger'}
console.log(color)// red
console.log(animal)// lion
As you can see in the above example, var
modified the variable in the global scope, while let
did not.
Additionally, neither let
nor const
can be redeclared once the variable is declared in a scope.
// This is validvar bird ='robin'var bird ='bluejay'// Uncaught SyntaxError: Identifier 'animal' has already been declaredlet animal ='wolf'let animal ='werewolf'
The difference between let
and const
is that let
can be reassigned and const
cannot.
// This is validlet x =1
x =2// VM159:5 Uncaught TypeError: Assignment to constant variable.const y =1
y =2
Ultimately, it depends on the situation when to use let
, var
, or const
, but the common convention is to use const
as much as possible, and let
in all other instances. Most new codebases do not include var
. Throughout the rest of this article, const
will be used by default.
String
Template Literals
Template literals, sometimes referred to as template strings, are a new type of string syntax that allows embedded expressions and multi-line strings. Template literals use a backtick (`) instead of double or single quotes.
Instead of concatenating with the string concatenation operator (+
), you can embed a variable directly in a string using ${}
.
// ES5const name ='Hal'const sentence ="I'm afraid I can't do that, "+ name +'.'
console.log(sentence)// "I'm afraid I can't do that, Hal."// ES6const name ='Hal'const sentence =`I'm afraid I can't do that, ${name}.`
console.log(sentence)// "I'm afraid I can't do that, Hal."
A string can also span multiple lines.
const haiku =`Over the wintry
forest, winds howl in rage
with no leaves to blow.`
console.log(haiku)// Over the wintry// forest, winds howl in rage// with no leaves to blow.
Note that white space, such as with indentation, is preserved using multi-line strings.
Methods
Several new String
methods were added in ES6: startsWith()
, endsWith()
, includes()
, and repeat()
.
String.prototype.startsWith()
We can easily determine if something begins a string using startsWith()
.
const word ='Disestablishmentarianism'
word.startsWith('Disestablish')// true
String.prototype.endsWith()
We can similarly determine if something ends a string using endsWith()
.
const word ='Disestablishmentarianism'
word.endsWith('ism')// true
String.prototype.includes()
includes
is a particularly helpful addition to the JavaScript language, and allows a very simple and straightforward way to find if a string contains a particular value at any point.
const word ='Disestablishmentarianism'
word.includes('establishment')// true
String.prototype.repeat()
The repeat()
method repeats and concatenates a string.
const word ='Beetlejuice '
word.repeat(3)// "Beetlejuice Beetlejuice Beetlejuice "
Functions
Arrow Functions
An arrow function expression is a new way to create a function expression in JavaScript.
// Function expressionconstreturnsTrue=function(){returntrue}// Arrow function expressionconstreturnsTrue=()=>{returntrue}
Arrow functions do not have their own this
, and cannot be used as a constructor. All arrow functions are anonymous, meaning they do not have a name.
Implicit Returns
Arrow functions also have the ability to utilize implicit returns, in which the return
statement as well as the block are omitted, and only the return value follows the arrow.
constreturnsTrue=()=>true
Parameters
If an arrow function only has one parameter, the parentheses can be omitted.
constsquare= num =>{ return num * num
}square(3)// 9
However, if more than one parameter is used, parentheses are once again mandatory.
constadd=(x, y)=>{ return x + y
}add(7,20)// 27
Default Parameters
Functions can now be initialized with default parameters.
// ES5functionsum(a, b){
b = b === undefined ?2: b;// option 1
b =2|| b // option 2return a + b
}// ES6functionsum(a, b =2){return a + b
}sum(5)// 7
Classes
JavaScript is a prototype-based language, not a class-based object-oriented system like Java or other similar languages. However, classes are a common architecture that many programmers are familiar with, and ES6 introduced a new class
keyword as syntactic sugar over the already existing constructor pattern.
// ES5functionGraph(x, y){ this.x = x; this.y = y;}
Graph.prototype.getGraphAxis=function(){ return{x: x, y: y}}// ES6classGraph{ constructor(x, y){ this.x = x; this.y = y; } getGraphAxis(){ return{x, y} }}const graph1 =newGraph('Horizontal','Vertical')
Inheritance
ES6 also introduced the new extends
keyword, which is again a wrapper extending a prototype
to a new function. You can also use the super()
keyword to copy the constructor of the parent.
// ES5functionGraph3D(x, y, z){
Graph.call(this, x, y); this.z = z;}
Graph3D.prototype.constructor = Graph3D;
Graph3D.prototype = Object.create(Graph.prototype);
Graph3D.prototype.getGraph3DAxis=function(){ return{x: x, y: y, z: z}}// ES6classGraph3DextendsGraph{ constructor(x, y, z){ super(x, y) this.z = z
} getGraph3DAxis(){ return{x, y, z} }}const graph2 =newGraph3D('Latitude','Longitude','Normal')
Objects
ES6 introduced a few shortcuts for object literals and methods for objects.
Property Value Shorthand
If an object property has the same name as a variable, ES6 removes the need to write it out twice. The shorthand can be mixed with other properties that are not utilizing the shorthand.
const title ='The Matrix'const releaseYear =1999const leadActor:'Keanu Reeves'
// ES5const movie ={
title: title,
releaseYear: releaseYear,
lead: leadActor
}// ES6const movie ={
title,
releaseYear,
lead: leadActor
}
Method Definition Shorthand
Instead of explicitly declaring a function on a property, we can use the method shorthand.
// ES5const user ={
name:'Nick',
getUserName:function(){ returnthis.name
}}// ES6const user ={
name:'Nick', getUserName(){ returnthis.name
}}
user.getUserName()// "Nick"
Note that arrow functions cannot be used as object methods.
Computed Property Keys
Objects now have the ability to evaluate an expression as part of an object key. We can see it in this example, as we increment each property key by one.
let count =0const x ={ [count++]:'zero', [count++]:'one',}
console.log(x)// {0: "zero", 1: "one"}
Object.assign()
Object.assign()
can be used to merge an object or shallowly clone an object. In this example, two objects are being merged.
const first ={ first:'Luke'}const last ={ last:'Skywalker'}const jedi = Object.assign(first, last)
console.log(jedi)// {first: "Luke", last: "Skywalker"}
In this example, an object is being cloned.
const jediClone = Object.assign({}, jedi)
console.log(jediClone)// {first: "Luke", last: "Skywalker"}
Arrays
ES6 added some new iteration techniques and methods to arrays.
For…of
The for...of
syntax has been introduced for iterating over arrays and other iterable objects, such as strings, Maps, and Sets. This is different from for...in
, which iterates over properties of an object.
const friends =['Rachel','Joey','Ross','Phoebe','Monica','Chandler']
for(let friend of friends){
console.log(friend)}// Rachel// Joey// Ross// Phoebe// Monica// Chandler
Methods
A few methods were added to Array
in ES6: find()
, findIndex()
, entries()
, keys()
, and values()
.
Array.prototype.find()
The find()
Array method returns the first item value that satisfies a given function. In the below example, the function is looking for even numbers, so find()
will return the first even number.
const numbers =[55,2,3,16]functiongetEven(number){ return number %2===0}
numbers.find(getEven)// 2
Array.prototype.findIndex()
The findIndex()
Array method is similar to find()
, but it will return the first index of the matching value.
const numbers =[55,2,3,16]functiongetEven(number){ return number %2===0}
numbers.findIndex(getEven)// 1
Array.prototype.entries()
The entries()
Array method creates an Array iterator of key/value pairs.
const friends =['Rachel','Joey','Ross','Phoebe','Monica','Chandler']const iterator = friends.entries()// Array Iterator {}
You can use for...of
to retrieve these values.
for(let friend of iterator){
console.log(friend)}// [0, "Rachel"]// [1, "Joey"]// [2, "Ross"]// [3, "Phoebe"]// [4, "Monica"]// [5, "Chandler"]
Array.prototype.keys()
The keys()
Array method will create an Array iterator of the keys of an array.
const friends =['Rachel','Joey','Ross','Phoebe','Monica','Chandler']const iterator = friends.keys()// Array Iterator {}for(let friend of iterator){
console.log(friend)}// 0// 1// 2// 3// 4// 5
Array.prototype.values()
The values()
Array method will create an Array iterator of the values of an array.
const friends =['Rachel','Joey','Ross','Phoebe','Monica','Chandler']const iterator = friends.values()// Array Iterator {}for(let friend of iterator){
console.log(friend)}// Rachel// Joey// Ross// Phoebe// Monica// Chandler
Spread Syntax
ES6 introduces a new syntax known as spread, represented by three periods (...
) as a shortcut for expanding arrays.
Copying Arrays
Spread syntax is particularly useful for copying array values into a new array. In the example below, we have an array of users, and we create a new array of users based on the original array, with a few additions.
const users =['Richard','Gilfoyle','Dinesh']const moreUsers =[...users,'Erlich','Jian Yang']
console.log(moreUsers)// ["Richard", "Gilfoyle", "Dinesh", "Erlich", "Jian Yang"]
This is particularly beneficial because we’re creating a new array, as opposed to mutating an existing array.
Function Calls
You can use the spread syntax to use the elements of an array as arguments to a function. Prior to ES6, it was necessary to use apply()
to unpack the array values.
// ES5functionmultiply(a, b){ return a * b
}const numbers =[5,6]
multiply.apply(null, numbers)// 30// ES6functionmultiply(a, b){ return a * b
}const numbers =[5,6]multiply(...numbers)// 30
Rest Parameters
Previously, you could send as many arguments as you wanted to a function in JavaScript, but only the values that corresponded to a parameter would be registered. Now, you can use the rest parameter syntax (...
) to put an indefinite amount of values into an array.
In the example below, only a
and b
are explicitly defined, so only 4
and 8
will go into their own variable. The rest of the values will be placed into an array called theRest
.
functionsum(a, b,...theRest){
console.log(a)// 4
console.log(b)// 8
console.log(theRest)// [15, 16, 23, 42]}sum(4,8,15,16,23,42)
Destructuring
Destructuring is a new concept introduced in ES6 that simplifies the process of taking array values or object properties and putting them into their own variables.
Object Destructuring
Creating variables from object properties is much quicker and more efficient in ES6.
const route ={
title:'Users',
path:'/users',
component: UserPage,}const{ title, path, component }= route
console.log(title)// "Users"
console.log(path)// '/users'
console.log(component)// [ Function ]
Array Destructuring
Similarly, values can be pulled from an array and set to individual variables.
const beatles =['John','Paul','George','Ringo']const[john, paul, george, ringo]= beatles
console.log(john)// "John"
console.log(paul)// "Paul"
console.log(george)// "George"
console.log(ringo)// "Ringo"
Promises
Promises are a way to handle asynchronous data synchronously. Before promises were introduced, JavaScript developers needed to use callbacks to deal with a function that relies on the completion of another function. A callback is a function passed to another function as an argument. Using promises, developers can now use the then()
syntax to work with data returned by an asynchronous call.
In this example, we create a Promise
and simulate an asynchronous call, such as an API call to a database to obtain a user, followed by a function that uses that data. A promise comes with resolve
and reject
, for the successful completion of the promise, or the failure.
const getUser =newPromise(function(resolve, reject){ setTimeout(function(){ const user ='Ivan' if(true){ resolve(user) }else{ reject('Error!') } },500)})functionprintUser(data){
console.log(data)}
The promise resolves because if (true)
will always be successful, and the data in resolve()
will become available to the next function after then()
. Below you can see how we call the promise, the next function, and the potential error.
getUser
.then(function(data){ printUser(data)}).catch(function(error){
console.log(error)})
If you change the code to if (false)
, you can see the catch()
function handling the error we created with reject()
.
Modules
It is now possible to use JavaScript modules to import and export variables, functions, and classes between files. In the browser, you can test modules by creating a simple HTML file with an export.js
and import.js
script, the import being of type “module”.
<!-- index.html --><!doctype html><htmllang="en"><head><metacharset="utf-8"><title>Testing Imports and Exports</title></head><body><scriptsrc="export.js"></script><scripttype="module"src="import.js"></script></body></html>
In export.js
, you can create some datatypes and a function to send.
// export.jsconst string ='Ivan'const array =[1,2,3]constsum=(a, b)=> a + b
export{ string, array, sum }
You can now receive those values in import.js
.
import{ string, array, sum }from'./export.js'
console.log(string)// "Ivan"
console.log(array)// [ 1, 2, 3 ]
console.log(sum(5,6))// 11
In this example, we exported multiple items, and it was necessary to use curly brackets to export and extract them. You can also create a default export.
// export.jsconstsum=(a, b)=> a + b
exportdefault sum
// import.jsimport sum from'./export.js'
console.log(sum(5,6))// 11
Conclusion
In this article, we covered many of the new features, syntax, and upgrades from ES5 to ES6. This includes new variable types let
and const
, classes, template literals, new String and Array methods, new Object shorthand syntax, arrow functions, rest and spread syntax, destructuring, promises, and modules. With this reference, you should be able to read, write, and understand the syntax of the next generation of JavaScript.