INTRODUCTION
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
• 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
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 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();
2) PROMISES
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));
2.5) ASYNC/AWAIT
If you are familiar with C#’s Async/Await then the JavaScript version will feel familiar.
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();
3) STRING INTERPOLATION
var subject = "world";
alert(`hello ${subject}`);
4) DEFAULT PARAMETERS
function sayGreetingAlert(subject, greeting = "hello"){
alert(`${greeting} ${subject}`);
}
sayGreetingAlert("world");
5) CLASSES
class Planet{
constructor(planetName = 'Planet'){ this.__name = planetName; }
greet(){ return `Hello ${this.__name}`; }
}
var myPlanet = new Planet('World');
alert(myPlanet.greet());
6) GETTERS AND SETTERS
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);
7) LET & CONST
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
Now in ES6, its easy and guaranteed because a run-time error occurs if a value change is attempted.
const title = 'Hello World';
alert(title);
title = 'Hi World'; //run-time error
alert(title); //this never executes due to the run-time error
8) DESTRUCTURING ASSIGNMENT
Destructing assignment also works with 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);
9) FOR OF
var btns = document.getElementsByTagName('input');
for(let btn of btns)
btn.addEventListener("click", () => alert("Clicked " + btn.id));
10) MODULES
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).
The module system can be further enriched with builders like webpack or rollup.js, since they use the ES6 module syntax and will resolve any dependencies and build them into one or more files. However, note that in HTTP/2, you can actually achieve better performance by NOT bundling! The topic of HTTP/2 warrants its own future article!
export function greet(){
alert('Hello World');
}
<!DOCTYPE html>
<html>
<head></head>
<body>
<script type="module">
import {greet} from './helloWorldModule.js';
greet();
</script>
</body>
</html>