Welcome to another tutorial, here you will learn about the new features in the latest version of JavaScript.
ECMAScript 2015 (ES6) is the sixth and major edition of the ECMAScript language standard as it defines the standard for JavaScript implementation.
ES6 brought about a huge change to the JavaScript language. The ES6 introduces numerous new features such as block-scoped variables, a new loop for iterating over arrays and objects, template literals, and other enhancements to make JavaScript programming simpler and exciting.
In this tutorial, we will discuss some of the best ES6 features that you can use in your everyday JavaScript coding.
In declaring variables, ES6 introduced the new ‘let’ keyword. Before the emergence of the ES6, in JavaScript, the only way to declare a variable was the var keyword.
Now, what are two critical differences between the var and let.
Block scoping implies that a new scope is created between a pair of curly brackets i.e. {}. Hence, if you declare a variable with the let keyword inside a loop, it does not exist outside of the loop, as illustrated in the example below:
// ES6 syntax
for(let i = 0; i < 5; i++) {
console.log(i); // 0,1,2,3,4
}
console.log(i); // undefined
// ES5 syntax
for(var i = 0; i < 5; i++) {
console.log(i); // 0,1,2,3,4
}
console.log(i); // 5
From the example above you can see that the variable i in the first block is not accessible outside the for loop. However, it enables us to reuse the same variable name multiple times as its scope is limited to the block ({}), which results in less variable declaration and cleaner code.
The new const keyword allows you to define constants. However, constants are read-only, as such you cannot reassign new values to them. They are block-scoped just like let. But, you can still change object properties or array elements:
const PI = 3.14;
console.log(PI); // 3.14
PI = 10; // error
Somehow, you can still change object properties or array elements:
// Changing object property value
const PERSON = {name: "Peter", age: 28};
console.log(PERSON.age); // 28
PERSON.age = 30;
console.log(PERSON.age); // 30
// Changing array element
const COLORS = ["red", "green", "blue"];
console.log(COLORS[0]); // red
COLORS[0] = "yellow";
console.log(COLORS[0]); // yellow
The for...of loop makes it possible for us to iterate over arrays or other iterable objects very easily. More so, the code inside the loop is executed for each element of the iterable object. This is shown here:
// Iterating over array
let letters = ["a", "b", "c", "d", "e", "f"];
for(let letter of letters) {
console.log(letter); // a,b,c,d,e,f
}
// Iterating over string
let greet = "Hello World!";
for(let character of greet) {
console.log(character); // H,e,l,l,o, ,W,o,r,l,d,!
}
The for...of loop cannot work with objects because they are not iterable. But, if you want to iterate over the properties of an object in your coding you can use the for-in loop.
Template literal makes it possible to create multi-line strings and perform string interpolation. So, with it, you can embed variables or expressions into a string at any spot without any form of difficulty.
The Template literals can be created by using back-tick (` `) (grave accent) characters instead of the usual double or single quotes. Therefore, variables or expressions can be placed inside the string by using the ${...} syntax. You can compare the examples and see how much useful it is:
// Simple multi-line string
let str = `The quick brown fox
jumps over the lazy dog.`;
// String with embedded variables and expression
let a = 10;
let b = 20;
let result = `The sum of ${a} and ${b} is ${a+b}.`;
console.log(result); // The sum of 10 and 20 is 30.
Whereas using the ES5 to achieve the same outcome, we had to write something like this:
// Multi-line string
var str = 'The quick brown foxnt'
+ 'jumps over the lazy dog.';
// Creating string using variables and expression
var a = 10;
var b = 20;
var result = 'The sum of ' + a + ' and ' + b + ' is ' + (a+b) + '.';
console.log(result); // The sum of 10 and 20 is 30.
Now, default values to the function parameters can be specified in ES6. This is to say that if no arguments are provided to the function when it is invoked, these default parameter values will be used. This feature is one of the most awaited ones in JavaScript. This is an example:
function sayHello(name='World') {
return `Hello ${name}!`;
}
console.log(sayHello()); // Hello World!
console.log(sayHello('John')); // Hello John!
Whereas using the ES5 to achieve the same outcome, we had to write something like this:
function sayHello(name) {
var name = name || 'World';
return 'Hello ' + name + '!';
}
console.log(sayHello()); // Hello World!
console.log(sayHello('John')); // Hello John!
Arrow Functions are another interesting feature in ES6 to look into. The function provides a simple and concise syntax for writing function expressions by opting out of the function and returning keywords.
Arrow functions are defined using a new syntax, the fat arrow notation ( i.e ‘=>’). The example below shows us how it looks:
// Function Expression
var sum = function(a, b) {
return a + b;
}
console.log(sum(2, 3)); // 5
// Arrow function
var sum = (a, b) => a + b;
console.log(sum(2, 3)); // 5
From the above example, you can see that there is no function and return keyword in the arrow function declaration.
However, you can also skip the parentheses i.e ‘()’ in a case when there is exactly one parameter, but you will always need to use it when there is zero or more than one parameter.
Furthermore, when there's more than one expression in the function body, you need to wrap it in braces ({}). Here you use the return statement to return a value.
We have numerous variations of how you can write arrow functions. Here are the most used ones:
// Single parameter, single statement
var greet = name => alert("Hi " + name + "!");
greet("Peter"); // Hi Peter!
// Multiple arguments, single statement
var multiply = (x, y) => x * y;
alert(multiply(2, 3)); // 6
// Single parameter, multiple statements
var test = age => {
if(age > 18) {
alert("Adult");
} else {
alert("Teenager");
}
}
test(21); // Adult
// Multiple parameters, multiple statements
var divide = (x, y) => {
if(y != 0) {
return x / y;
}
}
alert(divide(10, 2)); // 5
// No parameter, single statement
var hello = () => alert('Hello World!');
hello(); // Hello World!
There is a pertinent difference that exists between regular functions and arrow functions. An arrow function does not have it's own ‘this’, it takes ‘this from the outer function where it is defined, unlike a normal function. In JavaScript, it is the current execution context of a function.
For better understanding let's check out the examples:
function Person(nickname, country) {
this.nickname = nickname;
this.country = country;
this.getInfo = function() {
// Outer function context (Person object)
return function() {
// Inner function context (Global object 'Window')
alert(this.constructor.name); // Window
alert("Hi, I'm " + this.nickname + " from " + this.country);
};
}
}
var p = new Person('Rick', 'Argentina');
var printInfo = p.getInfo();
printInfo(); // Hi, I'm undefined from undefined
Now, rewriting the same example but with the ES6 template literals and arrow function:
function Person(nickname, country) {
this.nickname = nickname;
this.country = country;
this.getInfo = function() {
// Outer function context (Person object)
return () => {
// Inner function context (Person object)
alert(this.constructor.name); // Person
alert(`Hi, I'm ${this.nickname} from ${this.country}`);
};
}
}
let p = new Person('Rick', 'Argentina');
let printInfo = p.getInfo();
printInfo(); // Hi, I'm Rick from Argentina
From the example, you can see, that the keyword refers to the context of the function enclosing the arrow function which is Person object ( in line no-9), quite different from the previous example where it referred to the global object Windows (in line no-9).
In ES5 and earlier, classes never existed in JavaScript. It was introduced in ES6 which looks similar to classes in other object-oriented languages, such as PHP, Java, etc., but they do not work exactly the same way. The classes in ES6 make it easier to create objects, implement inheritance by using the extended keyword and reuse the code.
The ES6 makes it possible for you to declare a class using the new class keyword followed by a class name. By convention class names are written in TitleCase (i.e by capitalizing the first letter of each word).
class Rectangle {
// Class constructor
constructor(length, width) {
this.length = length;
this.width = width;
}
// Class method
getArea() {
return this.length * this.width;
}
}
// Square class inherits from the Rectangle class
class Square extends Rectangle {
// Child class constructor
constructor(length) {
// Call parent's constructor
super(length, length);
}
// Child class method
getPerimeter() {
return 2 * (this.length + this.width);
}
}
let rectangle = new Rectangle(5, 10);
alert(rectangle.getArea()); // 50
let square = new Square(5);
alert(square.getArea()); // 25
alert(square.getPerimeter()); // 20
alert(square instanceof Square); // true
alert(square instanceof Rectangle); // true
alert(rectangle instanceof Square); // false
From the example above, the Square class inherits from Rectangle using the extends keyword. However, classes that inherit from other classes are referred to as child classes or derived classes.
Here, you must call super() in the child class constructor before you can access the context (this). For example, if you happen to omit the super() and call the getArea() method on square object it will result in an error since getArea() method requires access to this keyword.
Note: Class declarations are not hoisted, unlike function declarations. It resides in the temporal dead zone (TDZ) until the execution reaches the point of the class declaration, like the ‘let’ and ‘const’ declarations. Therefore, the class has to be declared before accessing it, otherwise, a ReferenceError will occur.
Before the ES6, there was no native support for modules in JavaScript. In JavaScript applications everything inside it, for instance, variables across different JavaScript files shared the same scope.
The ES6 introduces a file-based module, where each module is represented by a separate ‘.js’ file. Therefore, the export or import statement in a module to export or import variables, functions, classes, or any other entity to/from other modules or files.
Now, let's create a module (i.e a JavaScript file "main.js") and place the following code in it:
let greet = "Hello World!";
const PI = 3.14;
function multiplyNumbers(a, b) {
return a * b;
}
// Exporting variables and functions
export { greet, PI, multiplyNumbers };
Also, let’s create another JavaScript file ‘app.js’ with the codes:
import { greet, PI, multiplyNumbers } from './main.js';
alert(greet); // Hello World!
alert(PI); // 3.14
alert(multiplyNumbers(6, 15)); // 90
Lastly, you can create an HTML file 'test.html' with the following code and open this HTML file in your browser using the HTTP protocol (or the localhost). You will also notice the type=" module" on the script tag.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ES6 Module Demo</title>
</head>
<body>
<script type="module" src="app.js"></script>
</body>
</html>
The ES6 introduces rest parameters which allow us to pass an arbitrary number of parameters to a function in the form of an array. However, it is particularly helpful in situations where you want to pass parameters to a function but you have no idea how many you will need.
The rest parameter can be specified by prefixing a named parameter with rest operator (...) (i.e three dots). It can only be the last one in the list of parameters, and there can only be one rest parameter. Let’s see how it works in the example below:
function sortNames(...names) {
return names.sort();
}
alert(sortNames("Sarah", "Harry", "Peter")); // Harry,Peter,Sarah
alert(sortNames("Tony", "Ben", "Rick", "Jos")); // John,Jos,Rick,Tony
In a case when the rest parameter is the only parameter in a function, it gets all the arguments passed to the function, or else, it gets the rest of the arguments that exceed the number of named parameters.
function myFunction(a, b, ...args) {
return args;
}
alert(myFunction(1, 2, 3, 4, 5)); // 3,4,5
alert(myFunction(-7, 5, 0, -2, 4.5, 1, 3)); // 0,-2,4.5,1,3
Note: please]do not misinterpret the term rest parameters with the acronym REST (meaning REpresentational State Transfer), and it has nothing to do with RESTful web services.
The spread operator denoted by (...), performs the exact opposite of the rest operator function. The spread operator spreads out (i.e it splits up) an array and passes the values into the specified function, as shown in the examples below:
function addNumbers(a, b, c) {
return a + b + c;
}
let numbers = [5, 12, 8];
// ES5 way of passing array as an argument of a function
alert(addNumbers.apply(null, numbers)); // 25
// ES6 spread operator
alert(addNumbers(...numbers)); // 25
You can as well use the spread operator to insert the elements of an array into another array without using the array methods like push(), concat(), unshift(), etc.
let pets = ["Cat", "Dog", "Parrot"];
let bugs = ["Ant", "Bee"];
// Creating an array by inserting elements from other arrays
let animals = [...pets, "Tiger", "Wolf", "Zebra", ...bugs];
alert(animals); // Cat,Dog,Parrot,Tiger,Wolf,Zebra,Ant,Bee
The destructuring assignment is an expression that can be used at ease to extract properties from objects or values from arrays into distinct variables by providing a shorter syntax.
We have two kinds of destructuring assignment expressions:
let's see how each of them works in the example:
Before ES6, to get an individual value of an array you need to write something like this:
// ES5 syntax
var fruits = ["Apple", "Banana"];
var a = fruits[0];
var b = fruits[1];
alert(a); // Apple
alert(b); // Banana
But, in ES6, you can do the same thing in just one line with the use of an array destructuring assignment:
// ES6 syntax
let fruits = ["Apple", "Banana"];
let [a, b] = fruits; // Array destructuring assignment
alert(a); // Apple
alert(b); // Banana
However, the rest operator can be used in the array destructuring assignment, as shown below:
// ES6 syntax
let fruits = ["Apple", "Banana", "Mango"];
let [a, ...r] = fruits;
alert(a); // Apple
alert(r); // Banana,Mango
alert(Array.isArray(r)); // true
In ES5 to extract the property values of an object you are required to write something like this:
// ES5 syntax
var person = {name: "Peter", age: 28};
var name = person.name;
var age = person.age;
alert(name); // Peter
alert(age); // 28
Whereas, in ES6, you can extract an object's property value and assign it to the variables easily as shown here:
// ES6 syntax
let person = {name: "Peter", age: 28};
let {name, age} = person; // Object destructuring assignment
alert(name); // Peter
alert(age); // 28
Most of the features discussed above are supported in the latest version of the major web browsers including Google Chrome, Microsoft Edge, Mozilla Firefox, Safari, etc.
Alternatively, the online transpilers (source-to-source compilers) like Babel free of cost to transpile your current ES6 code to ES5 for better browser compatibility without leaving out the benefits of enhanced syntax and capabilities of ES6.