In June 2015, there was a significant update to JavaScript, ushering in tons of new features including arrow functions, class destruction, and template strings, and more. Over the past years, these features continue to be updated to make your work with JS easier.
We don’t want you to be left behind with old ES5 code, so today let’s walk you through the most important updates to JavaScript since 2015 with an introduction on how to use each feature.
Here is what we will cover today:
Get hands-on practice with all the newest features you’ll need to land a JavaScript interview
JavaScript (JS) is a lightweight, object-oriented, interpreted programming language. In 1996, Netscape submitted JS to ECMA International to be standardized, which lead to a new version of the language called ECMAScript.
It took nearly 15 years for ECMAScript to see major changes, and since then, it has been updated regularly. Let’s briefly outline the history of ECMAScript updates over the past decade.
The first standardized version of ECMAScript was released in 1997. ECMAScript 2 followed a year later, bringing minor changes to modernize the language with ISO standards.
ECMAScript 3 was released in 1999 and ushered in many new popular features, including expression, try/catch exception handling, and more. After ECMAScript 3, no changes were made to the official standard for many years.
ECMAScript 4 was proposed as a significant upgrade in the mid-2000s. There was some controversy over these updates, and ES4 was scrapped.
ECMAScript 5 (ES5) came along in 2009 with subtle changes to ES3 so that JavaScript could be supported in all browsers. The desire for a more robust update began around 2012 when there was a stronger push to abandon support for Internet Explorer.
The next big update occurred in 2015 when ECMAScript 6 (ES6) or ECMAScript 2015 (ES2015) was officially released. ES6 features modernized JavaScript.
There have been four more updates since that time: ECMAScript 2016, 2017, 2018, and 2019. The name ES.Next is given to the upcoming version, which is still in revision and proposal.
In general, ECMAScript is the title used to refer to the formal standardized language, while JS is used when programmers discuss the language in practice.
Now that you have a sense of the evolving history of JavaScript, let’s jump right into the changes and additions we have seen over the years since 2015.
ES6 introduced the keywords let
and const
that enable us to declare variables much easier. Previously, variables declared with var
are function scoped, so if we declare them inside a for
loop they will be available outside of the loop.
Variables declared with let
and const
are block-scoped, which means they are only accessible within the block where they were declared. So, if we declare a variable with let
, it does not change its value in the outer scope. Const
is similar, but the value of variables declared with this keyword cannot change through reassignment.
// using `let`let x = "global";if (x === "global") {let x = "block-scoped";console.log(x);// expected output: block-scoped}console.log(x);// expected output: global
// using `var`var y = "global";if (y === "global") {var y= "block-scoped";console.log(y);// expected output: block-scoped}console.log(y);// expected output: block-scoped
There is no hard-and-fast rule about when to use which variables. Here are two different opinions from popular JavaScript developers on how to use these three variables.
From Mathias Bynes: Use
const
by default andlet
if rebinding is needed.var
should never be used in ES6.
From Kyle Simpson: Use
var
for top-level variables that are shared across many scopes. Uselet
for smaller scopes.
ES6 introduced arrows (=>
) as a shorthand way to declare functions. This update has three notable advantages:
.bind( )
methodreturn
There are some cases where you will want to avoid using arrow functions. You need to be careful when using it alongside the
this
keyword. Now, when you use the arrow function, thethis
keyword is inherited from the parent scope.
Here is an example of the new syntax.
var greeting = (name) => {console.log(`hello ${name}`);}
The updates to classes in ES6 do not introduce a new OO inheritance model. Instead, these classes are “syntactical sugar” to support prototype inheritance. This update is useful because it simplified your code without changing the basic model of JavaScript. It’s essentially a nicer, cleaner way of doing inheritance. You can create a class in two ways:
class
declarationclass
expressionYou will need the method constructor
to create a class
. Let’s take a look.
class Person {constructor(name,age){this.name = name;this.age = age;}greet(){console.log(`My name is ${this.name} and I am ${this.age} years old` );} // no commas in between methods}const sarah = new Person("Sarah",35);sarah.greet();
ES6 implemented the useful feature of template strings, now called template literals. This allows you to easily implement variables with a very simple syntax (${ }
) and embed expressions.
It’s particularly useful for constructing API requests and nesting templates.
${expression}
Take a look at an example below to see how template literals work:
let name = "Sarah";const greeting = `Hello my name is ${name}`;console.log(greeting);
Update your JavaScript knowledge with hands-on practice. Educative’s text-based courses feature embedded coding environments that allow you to skip the setup and start learning.
ECMAScript 2016 or ES7 brought with it two important updates to ES6 that are important to being a modern JavaScript programmer.
The .includes( )
method makes it easier for you to check if particular values are stored in an array. In the past, JavaScript developers had to use indexOf
and create a new function. But .include( )
will return true
if an array includes an element and false
if it does not. Take a look below to see it in action.
let array = [1,3,5,9];console.log(array.includes(2));// trueconsole.log(array.includes(3));// false
The exponential operator simplifies the way we do math in JavaScript. In the past, we had to use loop
, recursive functions, or Math.pow( )
, which could get pretty messy with each concatenation. But now, our code looks like this:
console.log(Math.pow(2,2));console.log(Math.pow(2,3));
These are two new ways of accessing our objects, resolving some of the limitations of Object.keys( )
, which return only the keys of the object. Now, Object.values( )
enables us to return an array of all the values of our Object, and Object.entries( )
returns an array that contains both keys and values.
const family = {father: "John Smith",mother: "Martha Smith",daughter: "Sarah Kent",}console.log(Object.values(family));console.log(Object.entries(family));// ["father", "John Smith"]// ["mother", "Martha Smith"]// ["daughter", "Sarah Smith"]
This ES8 update offers an alternative to callbacks and Promise and uses much more manageable syntax. The Async function allows us to define an asynchronous function and return a Promise. The .await( )
operator waits for a Promise inside the async function. Take a look at the new way of writing this code.
function resolveAfter2Seconds() {return new Promise(resolve => {setTimeout(() => {resolve('resolved');}, 2000);});}async function asyncCall() {console.log('calling');const result = await resolveAfter2Seconds();console.log(result);}asyncCall();
Let’s discuss the new code a bit more.
async
keywordnon-promise
, it returns a value wrapped inside a PromiseObject.getOwnPropertyDescriptors( )
This feature builds off of updates from ES6 so we can use rest/spread syntax for objects. The spread operator enables us to create a clone of an Object
so we can modify the original more easily.
This feature should not be used at the end, or it will result in an error. Take a look at the code below to see how it works.
let myObj = {a:1,b:3,c:5,d:8,}// we use the rest operator to grab everything else left in the object.let { a, b, ...z } = myObj;console.log(a); // 1console.log(b); // 3console.log(z); // {c: 5, d: 8}// using the spread syntax we cloned our Objectlet clone = { ...myObj };console.log(clone);// {a: 1, b: 3, c: 5, d: 8}myObj.e = 15;console.log(clone)// {a: 1, b: 3, c: 5, d: 8}console.log(myObj)// {a: 1, b: 3, c: 5, d: 8, e: 15}
This update enables you to use await
to declare asynchronous loops if the data comes from an asynchronous source. We use the for-await-of
to convert the iterables to a Promise.
The GitHub documentation explains that “an async iterator is much like an iterator, except that its next()
method returns a promise for a { value, done }
pair.” Take a look at the code below to see this in action.
const iterables = [1,2,3];async function test() {for await (const value of iterables) {console.log(value);}}test();// 1// 2// 3
Promise.prototype.finally ( )
The most recent updates of JavaScript add a few small but important features that you should know to be a modern JavaScript programmer.
This feature essentially flattens an array recursively up to a pre-specified depth. The flat() method makes a new array containing all sub-array elements. Infinity
is used to flatten nested arrays. Take a look at the code below to see it in action.
const letters = ['a', 'b', ['c', 'd', ['e', 'f']]];// default depth of 1console.log(letters.flat());// ['a', 'b', 'c', 'd', ['e', 'f']]// depth of 2console.log(letters.flat(2));// ['a', 'b', 'c', 'd', 'e', 'f']// which is the same as executing flat with depth of 1 twiceconsole.log(letters.flat().flat());// ['a', 'b', 'c', 'd', 'e', 'f']// Flattens recursively until the array contains no nested arraysconsole.log(letters.flat(Infinity));// ['a', 'b', 'c', 'd', 'e', 'f']
The method .description
allows you to return an optional description of a Symbol
object. Symbol
objects can have an optional description used for debugging purposes, and this new update enables you to read that description but does not contain the enclosing Symbol ( )
string.
const me = Symbol("Sarah");console.log(me.description);// "Sarah"console.log(me.toString());// "Symbol(Sarah)"
This method transforms your list of key-value pairs into objects, and we can pass any iterable as an argument of Object.fromEntries
. Take a look at the code below.
const keyValueArray = [['key1', 'value1'],['key2', 'value2']]const obj = Object.fromEntries(keyValueArray);console.log(obj);// {key1: "value1", key2: "value2"}
String.prototype.trimStart( )
/ trimEnd( )
Array.sort
Function.prototype.toString( )
Now you have been brought up to speed on all the JavaScript updates of the last years! There is still a lot to learn to get proficient with these new features and tools, so be sure to put them into practice and keep your eye out for more updates as the years go on.
If you’re interested in jumping right in, check out Educative’s course, The Complete Guide to Modern JavaScript. This course walks you through all the need-to-know information to master modern JavaScript with embedded coding environments, quizzes, and more.
Free Resources