| Ten Nifty ES6 JavaScript Features |

INTRODUCTION

In this article ten of my favorite features in ECMAScript 6 are showcased using basic “hello world” type programs. Each section contains plenty of links for more sophisticated examples/explanations, as well as a JSFiddle to try it out. Sections will expand on each other, in the sense that a feature talked about earlier on, for example arrow functions (#1), will be used later on – like in promises (#2).

While the ES6 standard isn’t exactly “new”, it’s not “old” either. Only since 2017 have the three major browsers (Chrome, Firefox, and Safari) been compatible with more than 98% of the standard (refer to kangax’s ECMAScript 6 compatibility table). So, it’s still “new” enough to talk these features in 2018 – except of course – if you have to support archaic browsers, like Internet Explorer.

BACKWARDS COMPATIBILITY

Luckily, there are a couple of options if you are required to support older browsers (like IE). They aren’t mutually exclusive, likely you will polyfill one feature and transpile another.

Modernizr – detects the capabilities of the browser and provides a polyfill library for functionality the browser does not support – check out Modernizr’s GitHub wiki for a comprehensive listing.

Babel – a Javascript compiler/transpiler (the semantics of what to refer to it as is largely opinion based). It will take JavaScript written against ES6 and convert to functionally equivalent ES5. You can add Babel in as part of a build step (for example grunt). Babel enables you to write your source code against ES6, but what goes to production is ES5. Then (hopefully) one day when you no longer need to support older browsers (IE), you can just remove the Babel build step and just deploy the source code as is!

1) ARROW FUNCTIONS

These work much like lambdas in C#/Java. They also yield a more intuitive usage of the “this” keyword inside the arrow function’s code block.

Per MDN’s web docs, in a typical function code block “the value of ‘this’ is determined by how [the] function is called. It can’t be set by assignment during execution, and it may be different each time the function is called”. However, when inside an arrow function (lambda) the “this” keyword will reference the outer context of where the arrow function was used.

In the following example, an arrow function is used for the setTimeout expression. It demonstrates the simpler syntax of using a lambda and that the “this” object intuitively refers to the same value of “this” as was defined in the scope of HelloWorld and not the window object – which is what would happen in ES5.

function HelloWorld(){
  this.greeting = "Hello World";  
  
  this.sayGreeting = function(){ 
    //'this' inside the setTimeout expression refers 
    //to the HelloWorld object instance, so the 'greeting' property 
    //on it is defined and alerts appropriately
    setTimeout(() => alert(this.greeting), 1000);
  }    
}

var myHelloWorld = new HelloWorld();
myHelloWorld.sayGreeting();
(For more information on “arrow functions” visit MDN’s Web Docs).

2) PROMISES

Native first class support! Supplementary libraries like Kriskowal’s Q are no longer required.

However, until existing libraries/APIs are retrofitted to support native Promises (like XMLHttpRequest), their callbacks will need to be wrapped. This can be done by using the Promise’s constructor and passing the callbacks for the various states, (like success) – follow this guide on MDN to do it.

var promise = new Promise(function(resolveFn){
  //setTimeout used as a pretend long running action
  setTimeout(() => { resolveFn('Hello World'); }, 1000);   
});

promise.then(result => alert(result));
(For more information on “promises” visit MDN’s Web Docs).

2.5) ASYNC/AWAIT

Okay, I’m cheating a bit on my list of my favorite ten ES6 features and Async/Await isn’t even a part of ES6 (its officially in ES8); however, all greenfield web browsers (those that have mechanisms for constant updates thus always support the latest features) currently work. It makes interacting with promises even more comfortable/powerful.

If you are familiar with C#’s Async/Await then the JavaScript version will feel natural.

async function sayHelloWorld(){
  var promise, result;

  promise = new Promise(function(resolveFn){
    //setTimeout used as a pretend long running action    
    setTimeout(() => { resolveFn('Hello World'); }, 1000);    
  });
  
  result = await promise;
  alert(result); //This alerts once the above promise resolves 
                 //because of the await keyword
}

sayHelloWorld();
(For more information on “async/await” visit MDN’s Web Docs).

3) STRING INTERPOLATION

Use the back-tick character ` around a string (instead of the normal one or two quotation characters) will enable the string to have variable substitutions in it. This is a neat way to no longer have to concatenate strings with variables!

var subject = "world";
alert(`hello ${subject}`);
(For more information on “string interpolation” visit MDN’s Web Docs).

4) DEFAULT PARAMETERS

In ES5, to default a parameter would have meant checking if the parameter value was undefined and if so, setting its value appropriately. However, now this can be accomplished just in the method’s signature; thus, giving a functionally equivalent solution with less code!

function sayGreetingAlert(subject, greeting = "hello"){
  alert(`${greeting} ${subject}`);
}

sayGreetingAlert("world");
(For more information on “default parameters” visit MDN’s Web Docs).

5) CLASSES

Full class support and inheritance! Granted it is actually syntactic sugar around the traditional ES5’s prototypal inheritance. Nevertheless, in ES6, it is easier to achieve the same results with less code. On top of being able to code in a familiar fashion (to that of more traditional OO languages).

class Planet{
  constructor(planetName = 'Planet'){ this.__name = planetName; }
  greet(){ return `Hello ${this.__name}`; }
}

var myPlanet = new Planet('World');
alert(myPlanet.greet());
(For more information on “classes” visit MDN’s Web Docs).

6) GETTERS AND SETTERS

These have actually been in since ES 5.1; however, the browser adoption wasn’t immediate. In ES6 Getters/Setters are fully supported as well as being available to use in classes.

class World {
  constructor(){
    this.__greeting = 'Hello World'; //default
  }
  set greeting(value){ this.__greeting = value; }
  get greeting(){ return this.__greeting; }
}

var myWorld = new World();
alert(myWorld.greeting);
(For more information on “getters and setters” visit MDN’s Web Docs).

7) LET & CONST

I’m cheating again here combining two features together, but they both deal with variable creation.

LET creates variables without having to worry about variable hoisting. As a general rule of thumb, variables should be declared using let, since it will produce functionality similar to that of existing object oriented languages like C#/Java.

In the following example the window will alert values 1, 0, 1, 2, 1 because the last alert still uses the initial value from when the variable “i” was declared, the “for” block hasn’t changed it. However, if the code was changed to use ES5’s “var” then the window would alert values 1, 0, 1, 2, 3 because the “for” block would have changed the value of i.

var i = 1;
for(let i=0; i<3; i++) //LET creating the loop's counter variable
  alert(i);

alert(i); //this alerts 1 because LET created the counter variable,
          //if VAR was used it would alert 3
(For more information on “let” visit MDN’s Web Docs).

CONST is an implementation of LET that prevents the value from changing once it is assigned (constant variable). In ES5 the best ways to achieve something similar, was either using a convention of all uppercase to denote not to change the value, or to use a closure. The closure would provide a method to access the “private” value (though this wasn’t full proof either since you could always just reassign the object providing the closure).

Now in ES6, its easy and guaranteed because a run-time error occurs if the value is attempted to be changed.

const title = 'Hello World';	
alert(title);  
title = 'Hi World'; //run-time error
alert(title); //this never executes due to the run-time error
(For more information on “constants” visit MDN’s Web Docs).

8) DESTRUCTURING ASSIGNMENT

This nifty feature makes assigning values from deeply nested objects, like those returned from web services (JSON), much easier! Most of the time I’m only interested in one piece out of a larger object and ES6’s destructing assignment provides a much better way to get it.

Destructing assignment also brings a technique similar to that in the default parameters section (#4). If a property does not exist, you can provide it with a default value, so you don’t need to worry about checking for undefined as you go through an object hierarchy to prevent run-time errors (check out the JSFiddle below for an example).

let myPlanet = { 'environment' : { 'water' : true } };

let { 
  environment : { 
    //our new 'myNewHasWaterVariable' variable
    //is being set to the water property under
    //the myPlanet.environment object hierarchy
    water : myNewHasWaterVariable  
  } 
} = myPlanet; //the object being "destructed"

alert(myNewHasWaterVariable);
(For more information on “destructuring assignment” visit MDN’s Web Docs).

9) FOR OF

This is an iterable type of loop, which can be thought of as a “foreach” kind of loop in C#/Java.

var btns = document.getElementsByTagName('input');    
for(let btn of btns)
  btn.addEventListener("click", () => alert("Clicked " + btn.id)); 
(For more information on “for of” visit MDN’s Web Docs).

10) MODULES

Native support for importing/exporting code files (modules) without needing supplemental functionality like requireJS. This is the trickiest one to demonstrate (no JSFiddle example is possible), so it was saved for last. It requires a web server to work (since modules are fetched with CORS restrictions).

For local development/testing, using Chrome, you can get an extension like chromebeat’s “Web Server For Chrome”, or follow the guide outlined in Justin Mathews’ article “Using Chrome as a Local Web Server” (I found it necessary to run incognito mode for this to work).

Other alternatives might be to use a builder like webpack or rollup.js, since that would let you use ES6 module syntax and webpack/rollup will resolve those dependencies and build them into one or more files. However, note that in HTTP/2, you actually achieve better performance by NOT bundling! This whole topic of HTTP/2 warrants its own future article!

export function greet(){
  alert('Hello World');
}
helloWorldModule.js
<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <script type="module">
      import {greet} from './helloWorldModule.js';
      greet();
    </script>
  </body>
</html>
index.html
(For more information on “modules” visit Mozilla Hacks’ “ES6 In Depth: Modules”).