Studyx - Professional web training

BECOME A WEB EXPERT

ECMAScript 6 en Babel

De finale versie van ECMAScript 6 (beter bekend als JavaScript, al hanteren we in dit artikel de naam EcmaScript of de afkorting ES) heeft de status van compleet gekregen in juni van dit jaar. Het omvat een hele boel handige nieuwe zaken en concepten, die de taal – naar mijn gevoel – een stuk eenvoudiger maken.

In dit artikel gaan we over enkele van de nieuwigheden die ES6 met zich meebrengt.

Babeljs

Maar voor we nieuwigheden kunnen bespreken moeten we stilstaan bij het fenomeen dat het altijd wat tijd kost voordat browsers zijn bijgebeend. Ik ben ervaren genoeg om de tijden te herinneren – IE6 wilde maar niet sterven – dat het soms jaren duurde totdat we “nieuwe” zaken konden gebruiken.

Gelukkig zijn deze tijden al lang achter ons en hoeven we deze keer niet te wachten. We hoeven zelf niet te wachten tot alle browsers de standaard hebben geïmplementeerd door gebruik te maken van een beetje magie die ook BabelJS heet.

BabelJS is wat men noemt een transpiler of in technische termen een source-to- source compiler. Met andere woorden: het neemt een bepaalde taal als invoer en als uitvoer krijg je een compleet andere taal.

Andere bekende voorbeelden zijn bijvoorbeeld CoffeeScript, Google Dart en Microsoft Typescript. Die nemen hun respectievelijke talen als invoer en als uitvoer krijg je bv javascript.

Babel neemt javascript die geschreven is in ES6 vorm en converteert die naar ES5 vorm. Dit maakt dat het ondersteund wordt door het gros van de moderne browsers. Internet explorer is bijvoorbeeld ondersteund vanaf versie 8 en hoger.

Babel is op zich technologie die zijn strepen al heeft verdiend en in gebruik is bij bedrijven zoals Netflix, Facebook, Paypal, Yahoo, Spotify en nog vele anderen!

Babel opzetten door middel van grunt

Voor dit artikel zullen we gebruik maken van Grunt om aan te tonen hoe een workflow eruit kan zien. Maar je kunt gerust gebruik maken van andere build systemen zoals bv Gulp, Webpack, Browserify en nog andere.

Ik maak gebruik van OSX maar deze stappen kunnen eenvoudig gereproduceerd worden op andere systemen. Ik ga er ook van uit dat men npm correct heeft geinstalleerd en geconfigureerd.

Eerst zullen we wat lege folders en bestanden aanmaken. Kopieer de volgende opdrachten in je terminal en voer ze uit.

mkdir es6project; cd es6project; mkdir src; mkdir dist; touch src/es6.js;
touch Gruntfile.js

We hebben natuurlijk ook Grunt and BabelJS nodig. Je kunt deze installeren door volgende opdrachten uit te voeren in je terminal.

npm install --save-dev grunt
npm install --save-dev grunt-babel

De volgende stap is dat we onze Grunt moeten configureren. Kopieer het volgende in het bestand Gruntfile.js en sla de wijzigingen op.

module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    babel: {
        options: {
            sourceMap: true
        },
        dist: {
            files: {
                'dist/es5.js': 'src/es6.js'
            }
        }
    }
});
  // Load the plugin that provides the "uglify" task.
  grunt.loadNpmTasks('grunt-babel');
  // Default task(s).
  grunt.registerTask('default', ['babel']);
};

Natuurlijk moeten we ook wat ES6 code hebben om mee te testen. Open het bestand es6.js die je kunt terugvinden in je src folder en kopieer het volgende stukje code en sla de wijzigingen op.

function hello(name="world"){
  console.log(`hello ${name}`);
}

hello();
hello("tom");

We kunnen de BabelJS taak (onze ES6 versie omzetten naar ES5) op eenvoudige wijze starten door de volgende opdracht in je terminal uit te voeren.

grunt

Wanneer we nu kijken in de dist folder zullen we een bestand vinden met de naam es5.js. Als je deze opent zul je zien dat hierin de ES5 versie is weggeschreven.

Als je nodejs hebt geinstalleerd staan kan je zelf de ES5 code eenvoudig testen. Je doet dit door volgende opdracht uit te voeren in je terminal.

node dist/es5.js

Nu is het voor jou mogelijk om ES6 code te schrijven zonder al te veel problemen!

In de volgende onderdelen zullen we kijken naar enkele nieuwe en vooral handige zaken die ES6 met zich meebrengt.

Constants

In ES5 was het mogelijk om een constant of een immutable variable te declareren zoals in volgend stukje code:

Object.defineProperty(window, 'PI', {
  value: 3.141593,
  writable: false,
  enumerable: true,
  configurable: false
});

ES6 komt met het const keyword wat het een stuk eenvoudiger maakt. We kunnen nu bovenstaande code herschrijven in 1 lijntje:

const PI = 3.141593

Natuurlijk, als je een variabele niet enuramble wilt maken, zul je nog altijd genoodzaakt naar de defineProperty moeten grijpen.

Let of block-scoped variabelen

Veel mensen hebben problemen met hoe ES omgaat met variabelen. Zeker in vergelijking met andere talen die (true) block scoping hebben. Het is een bron van vele bugs in JavaScript code.

Neem als voorbeeld volgend stukje code:

function varTest() {
    var answerOfLife = 42;

    if(true){
       var answerOfLife = 32;
       console.log(answerOfLife); // 32
    }

    console.log( answerOfLife); // 32
}

Wanneer je komt van een taal die true block scoping heeft, dan kan de uitkomst van bovenstaande code als raar worden ervaren om het nog vriendelijk uit te drukken.

In ES6 heeft men het let keyword geïntroduceerd dat het mogelijk maakt om variabelen te declaren die block-scoped zijn.

Laten we nu bovenstaand stukje code herschrijven door nu gebruik te maken van let.

function letTest() {
    let answerOfLife = 42;

    if(true){
       let answerOfLife = 32;
       console.log(answerOfLife); // 32
    }

    console.log( answerOfLife); // 42
}

De uitkomst is nu meer in lijn met wat je zou verwachten als je een ander block scoped taal zou gebruiken.

Template strings

Een van mijn favoriete zaken die men heeft toegevoegd in ES6 is template strings. Met template strings krijgen we o.a. multi-line strings en string interpolation.

Neem bijvoorbeeld volgend stukje code:

var poem = "violets are blue," +
           "roses are red," +
           "we're coming aboard," +
           "prepare to eat lead";

Door gebruik te maken van multi-line strings kunnen we dit herschrijven als volgt:

var poem = `violets are blue,
            roses are red,
            we're coming aboard,
            prepare to eat lead`;

String interpolation is een andere zaak die template strings met zich meebrengen. Het geeft ons de mogelijkheid om variabelen in te voegen in een string zonder gebruik te maken van het plus (+) symbool.

Neem nu volgend voorbeeld waar we een automatische support response genereren:

function automaticSupportResponse(name){
 console.log("Hello " + name + ". Have you tried turning it on or off
again ?"); 
}

automaticSupportResponse("kris");

Wanneer we echter gebruikmaken van string interpolation kunnen we dit herschrijven als volgt:

function automaticSupportResponse(name){
 console.log(`Hello ${name}. Have you tried turning it on or off again ?`);
}

automaticSupportResponse("kris");

Het werkt ook goed met objecten in Object Literal Notation:

var myMessage = { 
 recipient: "steve",
 message : "Have you tried turning it on or off again?" 
}

function automaticSupportResponse(msg){
 console.log(`Hello ${msg.recipient}. ${msg.message}`);
}

automaticSupportResponse(myMessage);

Pas op: om gebruik te kunnen maken van templates moet je het tick (` of accent grave) gebruiken in plaats van een single of double quote!

Object literals: property shorthand

Wat op zich best wel een lange term is om wat syntax suiker te beschrijven die het mogelijk maakt om in bepaalde gevallen keys te laten vallen.

Neem als voorbeeld volgend eenvoudig stukje code die een wagen voor ons aanmaakt:

function createCar(name, wheels, color){
  return { type: "car", name: name, wheels: wheels, color: color }
}

In ES6 kunnen we dit korter maken door de wheel en color keys te laten vallen:

function createCar(name, wheels, color){
  return { type: "car", name, wheels, color }
}

Object literals: computed property names

In ES5 was het al mogelijk om computed property names te gebruiken, zolang je het maar buiten de notatie van uw object deed.

myVehicle = {
  type: "car" 
}

myVehicle["wheels_" + type()] = 4; // wheels_car

In ES6 is het nu mogelijk om dit te bewerkstelligen binnen de notatie van uw object.

myVehicle = { 
  type: "car",
  ["wheels_" + type()] : 4 // wheels_car 
}

Object literals: method definition shorthand

De laatste toevoeging rond Object Literal Notation die we zullen bespreken is de method shorthand. Dit laat toe om de function keyword te laten vallen.

In ES5 zouden we volgende HeMan klasse kunnen definiëren die een simpele shout functie heeft:

var HeMan = { 
  shout: function(){
    return "I have the power"; 
  }
}

In ES6 kunnen we onszelf wat werk besparen door de function keyword te laten vallen.

var HeMan = { 
  shout(){
    return "I have the power"; 
  }
}

for … of

Vooraleer we gaan spreken over sets en maps is het belangrijk om het for…of statement te behandelen.

Het verschil tussen for…in en for…of kunnen we best aantonen door gebruik te maken van een eenvoudig voorbeeld.

We hebben een lijst met vijanden van batman en we zullen die lijst overlopen, eerst door gebruik te maken van for..in en dan for…of.

let batmanEnemies = ["joker", "harley quinn", "penguin", "riddler"];

for (let enemy in batmanEnemies){
  console.log(enemy); // logs: 0, 1, 2, 3
}

for (let enemy of batmanEnemies){
  console.log(enemy); // logs: joker, harley quinn, penguin, ....
}

Zoals je duidelijk kunt zien loopt the for..in over de property names (indexes in dit geval) en for…of over de property values.

Sets

Met de komst van ES6 kunnen we ook gebruik maken van het concept set. Een set is een data-structure die toelaat om een lijst van unieke waarden op te slaan.

Laten we als voorbeeld een spoken val object creëren dat een aantal spoken herbergt:

// define new set
let ghostTrap = new Set();

// add items to our set 
ghostTrap.add('vigor'); 
ghostTrap.add('slimer'); 
ghostTrap.add('puft marshmallow man');

// size 
console.log(ghostTrap.size); // 3

// test if set has an item
console.log(ghostTrap.has('slimer')); // true 
console.log(ghostTrap.has('brocolli worm')); // false

Een set overlopen is relatief eenvoudig wanneer we gebruikmaken van de for…of statement of gebruik maken van de forEach functie.

// iterate over items in set 
for (let ghost of ghostTrap){
  console.log(ghost); 
}

// iterating using the forEach method 
ghostTrap.forEach(function(ghost){
  console.log(ghost); 
});

Map

Een ander data-structure dat ES6 met zich meebrengt is de map. Het laat je toe een lijst te bouwen van diverse keys en hun values.
In het volgende voorbeeld hebben we een map die diverse instellingen opslaat.

// define new set
let mySettings = new Map();

// set items
mySettings.set('base_url', 'http://www.myurl.com'); 
mySettings.set('slogan', 'All your base are belong to us');

// size 
console.log(mySettings.size); // 2

// test if map has an item 
console.log(mySettings.has('base_url')); // true 
console.log(mySettings.has('color')); // false

Het over een map lopen verschilt niet zoveel met wat je doet bij een set:

// iterate over items in map
for (let [settingName, settingValue] of mySettings){
  console.log(`${settingName} = ${settingValue}`); 
}

// iterating using the forEach method 
mySettings.forEach(function(settingName, settingValue){
  console.log(`${settingName} = ${settingValue}`); 
});

Classes

Terwijl het concept van klasses niets nieuw is binnen ES, heeft men toch geopteerd om wat syntax suiker te introduceren die het een stuk eenvoudiger moet maken om ze te declareren.

Neem bijvoorbeeld volgende voertuig klas in ES5 vorm:

Function Vehicle(id, x, y){
  this.id = id;
  this.move(x,y);
}

Vehicle.prototype.move(x,y) {
  // move the vehicle
}

In ES6 kunnen we dit herschrijven door gebruik te maken van de nieuwe class keyword:

class Vehicle {
    constructor (id, x, y) {
        this.id = id;
        this.move(x, y);
    }

    move (x, y) {
      // move the vehicle
    }
}

Classes: inheritance

Met de komst van ES6 is het ook eenvoudiger om van een klasse te overerven.

Verder werkend op het voorbeeld van boven kunnen we nu dit doen:

class Car extends Vehicle{
   constructor(x,y){
      // call parent class constructor
      super("car", x, y);
   }

   honk(){
     console.log("honk");
   } 
}

var myCar = new Car();
myCar.move(10,20);
myCar.honk();

Classes: getters and setters

In ES6 is het ook mogelijk om gebruik te maken van getters and setters (geïntroduceerd in ES5.1) binnen de definitie van een klasse.

In het volgend voorbeeld hebben een simpele circle klasse die getters en setters heeft voor de straal (radius) en een getter voor de omtrek (circumference) :

class Circle {
   constructor(radius){
      this._radius = radius;
   }

   set radius(radius){
     this._radius = radius;
   }

   get radius(){
     return this._radius;
   }
   
   get circumference(){
     return 2 * 3.141593 * this._radius;
   } 
}

// init class
let myCircle = new Circle(10);

console.log(myCircle.radius); // (get radius) 10

myCircle.radius = 20; // (set radius)

console.log(myCircle.radius); // (get radius) 20

console.log(myCircle.circumference); // (get circumference) 125.6637...

Arrow functions (=>)

Een van de meest belangrijke toevoegingen is de introductie van arrow functions. Het is een verkorte vorm van wat men binnen Javascript een functional expression noemt.

Als je niet zoveel Javascript ervaring hebt of niet zo vertrouwd ben met de concepten, kan het mogelijk moeilijk zijn om te begrijpen wat ik bedoel met een functional expression.

Dus laat mij toe te starten met een simpel voorbeeld dat op zichzelf veel zal verklaren. We beginnen eerst met een functie in ES5 vorm waar het functional expression gedeelte de dubbele waarde van een getal zal retourneren.

let numbers = [1,2,3,4,5,6,7,8,9,10];

let doubled = numbers.map(function(number){
  return number * 2
});

console.log(doubled); // 2,4,6,8,10,12,14,16,18,20

In ES6 kunnen we dit een heel stuk korter maken door gebruik te maken van een arrow function (=>)

let numbers = [1,2,3,4,5,6,7,8,9,10];

let doubled = numbers.map(number => number * 2 );

console.log(doubled); // 2,4,6,8,10,12,14,16,18,20

Wat ook te vermelden waard is aan de JS liefhebber is dat arrow functions this impliciet binden en dat het altijd anonieme functies zijn.

Functions: default value parameters

In ES5 was het op zich al mogelijk om gebruik te maken van default value parameters, maar het was altijd wel wat rommelen.

Laten we als voorbeeld een functie schrijven die een nieuwe prijs berekent inclusief de taksen. Wanneer geen taks is gegeven gaat de functie uit van een taks die 21% bedraagt.

We starten met de ES5 versie:

function calculatePrice(price, tax){
  tax = tax || 0.21; // default 21%

  return price + (price * tax);
}

console.log(calculatePrice(500));

Als bovenstaande nog niet spannend genoeg was volgt hier de ES6 versie die – naar mijn gevoel – een heel stuk netter is:

function calculatePrice(price, tax = 0.21){
  return price + (price * tax);
}
console.log(calculatePrice(500));

Functions: rest parameters

De rest operator (…, drie puntjes) maakt het mogelijk een functie te declareren die kan aangeroepen worden met een variabel aantal argumenten. Als je ervaring hebt met Ruby of PHP (>=5.6) is het goed te weten dat dit te vergelijken is met wat men in die talen een splat operator noemt.

Neem als voorbeeld volgende functie die de som berekent van alle cijfers. Let ook op hoe we hier gebruik maken van de arrow function.

function sum(...numbers){
 var total = 0;

 numbers.forEach(number => total += number);

 return total;
}

console.log(sum(1,5)); // 6
console.log(sum(2,3,4)); // 9
console.log(sum(1,2,3,4,5)); // 15

Enkel nog maar de oppervlakte

Terwijl we veel nieuwe zaken hebben behandeld, zijn er nog ontzettend veel zaken die nog niet aan bod zijn gekomen.

Ik denk dan bvb aan de spread operator, generators, destructuring assignments, promises, proxies en nog een hele boel interessante zaken die ES6 met zich meebrengt en die zeker het bestuderen waard zijn.

Ik hoop ook dat het duidelijk is dat bovenstaande niet enkel en alleen toekomstmuziek is. Met BabelJS kun je het vandaag al beginnen toepassen en dat zonder al te veel problemen.

Share on FacebookShare on LinkedInTweet about this on TwitterShare on Google+Email this to someonePrint this page
22/10/15

0 Reacties opECMAScript 6 en Babel"

Laat een bericht na