Enhance Your JavaScript Skills with ES6 Features

Godspower AmanzeGodspower Amanze
15 min read

The ES6 syntax is arguably one of the most significant updates ever made in the evolution of the JavaScript ecosystem. It offers a series of modifications that simplify everyday programming operations and enhance your web development experience.
Whether you're an experienced programmer seeking to refresh your knowledge of the syntax, or a beginner–entirely new to the syntax, this document comprehensively describes all you need to get started using the ES6 syntax.

Introduction

ECMAScript version 6 (ES6)—also referred to as ECMAScript 2015—is a version of JavaScript which was released in 2015 as a major update to the JavaScript programming language.

Older JavaScript versions have had several challenges and limitations that made them less developer-friendly and harder to maintain for complex applications. However, the introduction of ES6 syntax to the JavaScript ecosystem has proven to be a game changer as it addresses the limitations experienced in the older versions of the language.

Knowledge of the ES6 syntax will enhance your ability to write codes that are clean, concise, easy to read, and maintain.
Discussed below are key ES6 modifications that will improve your JavaScript code:

  • The var, let and const keywords

    The current use of the let and const keywords provide solutions to the challenges of variable management associated with the fomerly used var keyword.
    The solutions provided are:

    • Block-scoped variables

      Before ES6, var declared variables only existed in either global or function scope.

      Global Scope:

        var name = 'Susan';
        console.log(name);// This outputs Susan
      

      Function Scope:

        function Greet(){
          var name = 'susan';
          console.log ('Hello ' + name)// This outputs Hello Susan
        }
      

      The let and const variables introduced in ES6 now provide something called a Block Scope.
      You create a Block scope by enclosing your code within curly braces {}. This ensures that variables can only be accessed within the code block where you declare them.

      For example

      Using let:

        let name = 'Jamie';// Here name is Jamie
      
        if (true){
          let name = 'Susan';
          console.log(name);// This outputs Susan
        }
      
        console.log(name);// This outputs Jamie
      

      Using const:

        const name = 'Jamie';// Here name is Jamie
      
        if (true){
          const name = 'Susan';
          console.log(name);// This outputs Susan
        }
      
        console.log(name);// This outputs Jamie
      

      Using var:

        var name = 'Jamie';// Here name is Jamie
      
        if (true){
          var name = 'Susan';
          console.log(name);// This outputs Susan
        }
      
        console.log(name);// This outputs Susan
      

      Block scope prevents unintended access to variables thereby helping to reduce scope related bugs.
      Variables declared using var do not have block scope therefore redeclaring a var variable within a block scope also redeclares and reassigns the variable globally.

    • Variable redeclaration and reassignment

      In contrast to the var keyword, variables declared with let and const cannot be redeclared within the same scope where you have previously declared them. Any attempt to redeclare let and const variables within the same scope returns a SyntaxError.
      Using let and const for variable declaration reduces the risk of variable conflict commonly associated with the var keyword.

      Using let:

        let name = 'Jamie';
        let name = 'May';
        console.log(name);// outputs a "SyntaxError: Identifier 'name' has already been declared"
      
        if (true){
          let name = 'Susan';
          let name = 'Shades';
          console.log(name);// outputs a "SyntaxError: Identifier 'name' has already been declared"
        }
      

      Using const:

        const name = 'Jamie';
        const name = 'May';
        console.log(name);// outputs a "SyntaxError: Identifier 'name' has already been declared"
      
        if (true){
          const name = 'Shades';
          const name = 'Susan';
          console.log(name);// outputs a "SyntaxError: Identifier 'name' has already been declared"
        }
      

      Using var:

        var name = 'Jamie';
        var name = 'May';
        console.log(name);// outputs May
      
        if (true){
          var name = 'Shades';
          var name = 'Susan';
          console.log(name);// outputs Susan
        }
      

      It is however worthy to note that while you can reassign a let variable, you cannot reassign a const variable. Variables declared using const are immutable and therefore their values remain constant.
      The "TypeError: Assignment to constant variable" is raised when you attempt to reassign a const variable.

      Using let:

        let name = 'Jamie';
        name = 'May';
        console.log(name);// outputs May
      
        if (true){
          let name = 'Susan';
          name = 'Shades';
          console.log(name);// outputs Shades
        }
      

      Using const:

        const name = 'Jamie';
        name = 'May';
        console.log(name);// outputs a "TypeError: Assignment to constant variable"
      
        if (true){
          const name = 'Shades';
          name = 'Susan';
          console.log(name);// outputs a "TypeError: Assignment to constant variable"
        }
      

      The use of let and const makes your code easy to understand and maintain as readers can easily identify variables that may change from those that will remain constant. This is seen as good practice and helps for easy collaboration with other developers.

  • Template literals

    Concatenation is the process of combining two or more strings to form a new string.
    Before ES6 was introduced, JavaScript developers concatenated strings and variables by using the addition operator (+). This often resulted in a messy and sometimes complicated code base.
    ES6 introduced template literals as a much simpler way to concatenate strings and variables.

    The introduction of template literals addressed pain-points in the following areas:

    • String concatenation

      Before ES6, developers built complex strings by doing this;

      var name = 'John'; // this is a regular string
      var surname = 'Doe';
      let greeting = 'Hello ' + name + surname + ' , nice to meet you.'
      console.log(greeting); // output = Hello John Doe, nice to meet you.
      

      This method of concatenation turns out to be not only tedious, but also produces codes that are hard to read and prone to errors. But with the introduction of template literals in ES6, you can now easily concatenate strings and/or variables by simply enclosing them within backticks.

      However, when substituting embedded expressions such as variables within the template string, enclose them within a dollar symbol and curly braces ${}. This substitution technique is called string interpolation.

      For example

      let name = `John`; // this is a template string
      let surname = `Doe`;
      let greeting = `Hello ${name} ${surname}, nice to meet you.`
      console.log(greeting); // output = Hello John Doe, nice to meet you.
      
    • Multiline strings

      Using template literals in ES6 allows you to also create multiline characters with ease and without the complexity of the newline character (\n) previously used in earlier JavaScript versions. In ES6, you can easily add new lines to your code just by hitting the enter key.

      ES5:

      var output = 'This is \n a newline \n character'

      ES6:

      const text = `
        This is
        a newline
        character`
      
  • Destructuring

    Destructuring in JavaScript is simply the unpacking or extraction of the values of an object or array into individual variables.

    • Object destructuring

      Consider the object:

        const object = {
          name: 'Douglas',
          age: 20,
          isMale: true
        }
      

      Before ES6, there were two prevalent methods of accessing object property values and assigning variable names to them.

      This:

        const name = object.name;
        const age = object.age;
        const isMale = object.isMale;
      
        console.log(name);// outputs Douglas
        console.log(age);// outputs 20
        console.log(isMale);// outputs true
      

      And this:

        const name = object['name'];
        const age  = object['age'];
        const isMale = object['isMale'];
      
        console.log(name);// outputs Douglas
        console.log(age);// outputs 20
        console.log(isMale);// outputs true
      

      But with the introduction of the destructuring syntax in ES6, you can now access object property values and assign variable names to them in a much simpler way by doing this:

        const {name, age, isMale} = object;
      
        console.log(name);// outputs Douglas
        console.log(age);// outputs 20
        console.log(isMale);// outputs true
      

      The syntax above destructures the object using the existing object properties as variable names. However, if you wish to assign a custom variable name to an object property value, separate the object property from your preferred custom variable using a colon (:).

      For example

        const {name:customName, age:customAge} = object;
      
        console.log(customName);// outputs Douglas
        console.log(customAge);// outputs 20
      

      The colon (:) reassigns the values of the name and age property to the customName and customAge variables respectively.

    • Array destructuring

      Consider the array:
      const array = ["Douglas", 20, true]

      Before ES6, developers accessed array elements and assigned variable names to them using their index value like this:

        const name = array[0];
        const age  = array[1];
        const isMale = array[2];
      
        console.log(name);// outputs Douglas
        console.log(age);// outputs 20
        console.log(isMale);// outputs true
      

      But with the ES6 destructuring syntax, you can now directly access array elements and assign your preferred variable names to them in a much simpler way by doing this:

        const [name,age,isMale] = array
      
        console.log(name);// outputs Douglas
        console.log(age);// outputs 20
        console.log(isMale);// outputs true
      

      If the array contains more elements than the variable names assigned, the rest of the unassigned array elements are discarded. However, if the array contains less elements than the declared variables, the unassigned variables are returned undefined.

      For example

        const [name,age,isMale,occupation] = array
      
        console.log(name);// outputs Douglas
        console.log(age);// outputs 20
        console.log(isMale);// outputs true
        console.log(occupation);// outputs undefined
      

      If you do not intend to use an element within the array, you may skip the position of the element using a comma.

        const [name,,isMale] = array
      
        console.log(name);// outputs Douglas
        console.log(isMale);// outputs true
      
  • Arrow function

    Arrow function is another significant improvement of ES6 over previous JavaScript versions. It earns its name from the striking resemblance of its basic syntax to an arrow head (=>). The arrow function feature provides a shorter and simpler way of writing function expressions compared to traditional functions.

    Before ES6, JavaScript developers wrote traditional functions using the function syntax like this:

      function functionName(parameter){
        return expression;
      }
    

    Using the ES6 arrow function syntax, you can now create functions like this:
    let functionName = (parameter) => {return expression}

    The function expression below returns the sum of two numbers:

    Using regular function:

      function add(parameter1, parameter2){
        return parameter1 + parameter2;
      }
    
      add(7,3)// outputs 10
    

    Using arrow function:

      let add = (parameter1, parameter2) => {
        return parameter1 + parameter2;
        }
    
      add(7,3)// outputs 10
    

    If the return expression doesn't span multiple lines, you can skip the return keyword and curly braces {} like this:

    let add = (parameter1, parameter2) => parameter1 + parameter2;

    If the function expression takes a single parameter, you can further simplify your code by omitting the parentheses as follows:

    let functionName = parameter => expression;

    The function below returns the cube value of its argument:

      let cubeVal = x => x**3;
    
      cubeVal(3)// outputs 27
    

    In the example above, note that the function stills runs perfectly without the parentheses (), curly braces {}, and return keyword.

  • Default function parameters

    With ES6 JavaScript, it is now possible to set default values for function parameters during function declaration. If you do not provide any arguments when calling the function, these default values will be used, ensuring that the function still runs as it should.

    Example 1

    The function BMI() below calculates the body mass index (BMI) of an individual:

      const BMI = (weight = 70, height = 1.73) => {
      return `your BMI is ${(weight / ((height**2).toFixed(2))).toFixed(2)}`}
    

    Note how the BMI() function above implements the ES6 arrow function, template literal, and default function parameter. The default values assigned to the weight and height parameters ensure that the function still returns a valid value if you do not explicitly pass an argument when calling the function.

    BMI()// your BMI is 23.41

    BMI(110,1.95)// your BMI is 28.95

    Example 2

    const identity = (name='Guest') =>{
      alert(`Hello ${name}`)
    }
    

    Running the identity() function above alerts the name of a hypothetical user accessing an online service through a guest account. The default parameter ensures that the code does not break even when a user isn't signed into their account.

    Identity()// Hello Guest

    A signed in user will get a custom 'Hello' message addressed to their user.

    Identity('Greek')// Hello Greek

  • Classes

    Classes are templates or blueprints for creating multiple instances of objects with similar properties. By using classes, you can create new objects that share common features without duplicating the code. Each instance of the class will have its own specific property values but still share the same methods and behaviour defined in the class.

  • How to create a class

    Follow the syntactic steps outlined below to create a class:

    • Define the class by using the class keyword followed by the class name and curly braces {}
    • Within the class, declare a constructor() method to initialize properties that will be shared by all instances of the class.
    • Add methods to the class for defining the behaviour and functionality of other object instances to be created from the class.
    • (Optional) Use the extends keyword to create a subclass that inherits properties and methods from a parent class.

      Example 1

      The expression below creates a simple Vehicle class that contains brandName, model, and yearOfProd properties.

        class Vehicle {
          constructor(brandName,model,yearOfProd) {
            this.brandName = brandName;
            this.model = model;
            this.yearOfProd = yearOfProd;
          }
      
          description(){
          return `This car is a ${this.brandName} ${this.model} produced in ${this.yearOfProd}`;
          }
      
          age(){
          let date = new Date();
          return `This ${this.brandName} was produced ${date.getFullYear()-this.yearOfProd} year(s) ago`;
          }
        }
      

      From the Vehicle class created above, you can now generate new object instances with unique property values by using the new keyword like this:

      let firstCar = new Vehicle('Tesla', 'Plaid', 2021)

      The code structure above automatically generates a firstCar object that looks like this:

        let firstCar = {
          brandName: "Tesla"
          model: "Plaid"
          yearOfProd: 2021
        }
      

      The firstCar object also has access to all the properties and methods in the Vehicle class.

        console.log(firstCar.brandName)// Tesla
        console.log(firstCar.model)// Plaid
        console.log(firstCar.yearOfProd)// 2021
        console.log(firstCar.description())// This car is a Tesla Plaid produced in 2021
        console.log(firstCar.age())// This Tesla was produced 2 year(s) ago
      

      You can also extend the properties and methods of a predefined (parent) class to a new class (subclass) using the extends keyword and super() method. The super() method of the subclass calls the constructor() method of the parent class to obtain access to the properties and methods defined within it.

      Example 2

      The expression below creates a SuperBikes class that extends the brandName, model, and yearOfProd properties of the Vehicle class, but also contains a new horsePower property of its own.

        class SuperBikes extends Vehicle {
          constructor(brandName, model, yearOfProd, horsePower) {
            super(brandName, model, yearOfProd);
            this.horsePower = horsePower;
        }
      
          bikeDesciption(){
            return `This is a ${this.brandName} ${this.model}. It was produced in ${this.yearOfProd} and has ${this.horsePower} horsePower.`
          }
        }
      

      You can now generate as many object instances of the SuperBike class as required.

      Example 3

      let firstBike = new SuperBikes('Suzuki', 'Hayabusa', 2012, 297)

      New objects will have access to the properties and methods of the SuperBike class, and by extension, those of the Vehicle class. This means that you can now do something like this:

        console.log(firstBike.age())// This Suzuki was produced 11 year(s) ago
      
        console.log(firstBike.bikeDesciption())// This is a Suzuki Hayabusa. It was produced in 2012 and has 297 horsePower.
      

      The template-like nature of JavaScript classes promotes code reusability, allowing you to easily write scalable and maintainable codes.

  • For of

    The for..of syntax is an ES6 looping construct used for performing iterations over a wide range of iterable data stuctures–this includes arrays, strings, NodeLists, and collections. It offers an intuitive way of handling loop operations without the complexities of index[i] management associated with the traditional for loop.

    syntax

      for (variable of iterable){
        statement to be executed..
      }
    

    To use the for..of syntax, initialize a variable with your preferred name using the let or const keyword. This variable is set to each value of the iterable object for every iteration and is subsequently utilized in the execution statement.

    Here are a few applications of the for..of syntax:

  • Iterating over an array

    Consider the following array:

    let scores = [23,35,15,40]

    The code below returns the sum of the total numbers in the scores array.

      let sum = 0
      for(const score of scores){
        sum+=score;
      }
      console.log("The total sum of the scores is: ", sum)//The total sum of the scores is: 113
    

    In this example, the for..of loop iterates through each number of the scores array. During each iteration, the score variable is set to the value of the current number in the array and then added to the sum.
    Finally, the code outputs the value of the sum, which represents the addition of all the elements in the array.

  • Iterating over a string

    Consider the string:
    const language = 'javascript'

    The code below iterates through each character of the string 'javascript', and logs to the console, any vowel character.

      const vowels = 'aeiou';
      for(const char of language){
        if(vowels.includes(char)){
        console.log(char);//aai
        }
      }
    
  • Iterating over a NodeList

    Iterating over items within a NodeList allows for versatile DOM manipulation.
    For example, consider a hypothetical NodeList containing all button elements in the DOM;

    let buttons = document.querySelectorAll('button');

    Using the for..of loop, you can dynamically add an event listener to each button element in the NodeList without having to manually do so one after the other.

      for(const button of buttons){
        button.addEventListener('click', () => {
          button.style.transform = 'translateY(1px)';
        });
      }
    

    The event listener listens for a click event and adds a css transform property on all buttons, providing the user with an interactive experience in where they appear to be pushed in when clicked.

  • Rest parameter

    The rest parameter allows you to create functions capable of accepting a variable number of arguments as an array, without explicitly defining them during function declaration.
    Only one rest parameter is allowed in a function definition, and it must be positioned as the final parameter within the function definition.

    syntax

    To create a rest parameter, prefix the parameter with three dots (...);

      const myFunction = (param1, param2, ...myRest) => {
        statement to be executed..
      }
    

    Conventionally, you must declare an argument as a parameter during function declaration before calling the function. This means that any argument you pass into the function call without having defined it during function declaration, the function will not execute it.

    For example

      const add = (a,b) => {
        return `the sum of numbers is ${a+b}`
      }
    

    add(5,7)//the sum of numbers is 12

    add(5,7,8,12)//the sum of numbers is 12

    However, with the rest parameter, you can create a function that executes any number of arguments passed into the function call;

    Example 1

      const add = (...rests) => {
        let sum = 0
        for(const rest of rests){
          sum+=rest
      };
        return `the sum of numbers is ${sum}`;
      }
    

    add(1,2,3,4,5)//the sum of numbers is 15

    add(10,20,30,40,60,90)//the sum of numbers is 250
    Rest parameters are array instances, therefore, you can execute instructions on them using array methods such as map(), filter(), sort(), reduce(), forEach(), and so on.

    Example 2: Using the map method

      const multiply = (...arguments) =>{
        return arguments.map(arg=>arg*2)
      }
    

    multiply(10,20,40,50)//outputs [20,40,80,100]

    Example 3: Using the forEach method

      const multiply = (...arguments) =>{
        return arguments.forEach(arg=>arg*2)
      }
    

    multiply(10,20,40,50)//outputs [20,40,80,100]

Conclusion

ES6 has become the industry-standard syntax in the JavaScript ecosystem due to it's widespread adoption and ongoing support. Proper knowledge of the syntax empowers you to write codes that are clean, concise, easy to read, and maintain. As the JavaScript ecosystem continues to evolve, embracing ES6 and subsequent JavaScript versions are essential to staying relevant in the modern web development practice.

0
Subscribe to my newsletter

Read articles from Godspower Amanze directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Godspower Amanze
Godspower Amanze

Front-end developer (React) and technical writer with a passion for solving business problems by transforming them into reasonable logic with clear and concise documentation.