ES6 / ES2015

and beyond …

ES6 / ES2015

and beyond …

Agenda für heute

  • ECMAScript im Allgemeinen
  • ES6 Features
  • Wie kann ich ES6 heute nutzen

Was ist ECMAScript?

JavaScript vs ECMAScript

  • JavaScript
    • die Programmiersprache
  • ECMAScript
    • Spezifikation des Sprachkerns
      • ​Nicht der APIs
      • Nicht die Browser und Node Spezifika

Wer schreibt die Spezifikation?

  • Nicht etwa das W3C oder WHATWG
  • Sondern Ecma TC39
    • genauer »Ecma International,
      Technical Committee 39 - ECMAScript«

Historie und Zukunft

ECMAScript versions

  • 1st Edition: Juni 1997
  • 2nd Edition: August 1998
  • 3rd Edition: Dezember 1999
  • 4th Edition: verworfen
  • 5th Edition: Dezember 2009
  • 6th Edition / 2015: Juni 2015

Näheres zur Historie:
Eine kurze Geschichte von JavaScript

Jedes Jahr eine neue Version

  • ECMAScript 7 / 2016

Ausblick auf ES8 / 2017 😱

Umstieg von ES5 nach ES6

What could possibly go wrong?!?

  • 100% ige Kompatibilität
    • ​Identische Einbindung
    • Kein ES6 Mode
      • ​automatisch ES5 'strict mode'
    • ES6 striktes Superset von ES5
      • keine Features entfernt
  • Häppchenweise anfangen
    • benutzen was einen interessiert

Features

Könnte man aufteilen nach:

  • Syntaktischer Zucker für bestehende Features
    • z.B. Classes
  • Neue Funktionalitäten in der standard library
    • z.B. Neue Methoden für Strings und Arrays
  • Komplett neue Features
    • z.B. Generators, Proxies, WeakMaps

let & const

Block scoped Variablen

// Assume age is 55

if (age > 50) {
  let dogAge = age * 7;
  let sentence = 'You’re pretty old. In dog years you are ' + dogAge;
  console.log(sentence); // → You’re pretty old. In dog years you are 385
}

console.log(sentence); // → ReferenceError: sentence is not defined

let

const company = 'Micromata';

// 42 locs later
company = 'Polyas';  // → TypeError: 'company' has already been declared

const

 Aber Constants sind nicht immutable 👻

const vinnie = {
  age: 0,
  weight: 3.5 
}

// Properties can change
vinnie.age = 1;
vinnie.weight = 12;

console.log(vinnie); // → Object {age: 1, weight: 12}

Merke!

  • Verwende wenn möglichst const
  • oder let
  • var nur falls explizit function level scope gewünscht

Template strings

String concatenation und mehr

'single' + vs + "double" + quotes

  • in JavaScript nur stylistische Geschmacksfrage
  • kein Unterschied in der Interpretation 
// The old way
var name = 'Hansi';
var age = 5;
var sentence = 'My cat ' + name + 'is ' + age + 'years old.';

console.log(sentence); // → My cat Hansi is 5 years old.

Bisher

// or even
var sentence = [];

sentence.push('My cat');
sentence.push(name);
sentence.push('is');
sentence.push(age);
sentence.push('years old.');

sentence = sentence.join(' ');

console.log(sentence); // → My cat Hansi is 5 years old.
// The ES6 way
const name = 'Hansi';
const age = 5;
const sentence = `My cat ${name} is ${age} years old.`;

console.log(sentence); // → My cat Hansi is 5 years old.

`backticks`

const person = {
  name: 'Michael',
  job: 'developer',
  city: 'Kassel',
};

const markup = `
  <div class="person">
    <h2>
      ${person.name} – 
      <span class="job">${person.job}</span>
    </h2>
    <p class="city">${person.city}</p>
  </div>
`;

console.log(markup);

Multi line strings 

// Classic string concatenation
let age = 55;
const dogAge = age * 7;
const sentence = 'You are ' + dogAge + ' years in dog years.';

console.log(sentence); // → You’re pretty old. In dog years you are 385


// Using ES6 template literals
let age = 55;
const sentence = `You are ${age * 7} years in dog years.`;

console.log(sentence); // → You’re pretty old. In dog years you are 385

Expressions

// Use existing functions
const bill = `your total bill is ${calculateBill(11.54)} including taxes.`

Functions

const username = 'Kai';
const age = Math.floor(Math.random()*100);

function query(queryParts, ...params) {
  const query = mysql.prepare(queryParts.join('?'), params);
  return query.execute();
}

query`UPDATE users SET age = ${age} WHERE username = ${username}`;

// Generiert:
// query:   'UPDATE users SET age = ? WHERE username = ?'
// payload: [42, 'Kai']

Tagged template literals

Fazit

  • Template literals erhöhen
    • Lesbarkeit 
    • Wartbarkeit
  • Tagged template literals sind mehr als syntaktischer Zucker

Arrow functions 

Making `this` good again

Syntaktisches

// Functions in ES5
var names = ['Hans', 'Emma'];

var fullNames = names.map(function(name){
  return `${name} Eichenfeld`; 
});

console.log(fullNames); // ["Hans Eichenfeld", "Emma Eichenfeld"]

Introduction

// Arrow functions in ES6
var names = ['Hans', 'Emma'];

// Replace `function(name)` with `(name) =>`
var fullNames = names.map((name) => {
  return `${name} Eichenfeld`; 
});

console.log(fullNames); // ["Hans Eichenfeld", "Emma Eichenfeld"]
// Arrow functions in ES6
var fullNames = names.map((name) => {
  return `${name} Eichenfeld`; 
});

Single arguments

// Lose the parens if you prefer
var fullNames = names.map(name => {
  return `${name} Eichenfeld`; 
});
// Lose `return` keyword and curly braces if you prefer
var fullNames = names.map(name => `${name} Eichenfeld`);

Implicit return

Compared to ES5 1nce again

// Implicit return an object literal
var fullNames = names.map(name => ({first: name, last: 'Eichenfeld'}) );

Returning object literals

var fullNames = names.map(function(name){
  return `${name} Eichenfeld`; 
});
const numbered = names.map((name, index) => `${name} is No. ${index + 1}`);

Multiple arguments

setTimeout(() => { console.log('done') }, 1000);

// vs ES5 way
setTimeout(function () {
  console.log('done');
}, 1000);

No arguments

this

Bei Verwendung von Arrow functions ändert sich der Wert von this nicht.

// ES5 binding of `this`
card.addEventListeneI('click', function() {
  // Add closing class to start closing animation
  this.classList.add('closing');
  
  // Add closed class after 500 milliseconds   
  setTimeout(function(){
    this.classList.add('closed'); // Cannot read property 'add' of undefined(…)
    console.log(this); // Window {external: Object, chrome: Object …}
  }, 500);

});

What’s `this`?

// ES5 »fix«
card.addEventListeneI('click', function() {
  var self = this;
  this.classList.add('closing');
  
  setTimeout(function(){
    self.classList.add('closed');
  }, 500);

});
// Lexical `this` with ES6 arrow functions
card.addEventListeneI('click', function() {
  // Add closing class to start closing animation
  this.classList.add('closing');
  
  // Add closed class after 500 milliseconds   
  setTimeout(() => {
    this.classList.add('closed'); // Works \o/
  }, 500);

});

Lexical scoping `this`

Promises

Asynchronität unter Kontrolle

Allgemein, Promises

  • kapseln asynchrone Ereignisse
  • verbergen so Komplexität vor dem Nutzer
  • bieten einen Synchronisationspunkt über .then()
  • verbessern Lesbarkeit von Code
  • vermeiden Pyramid-of-Doom
  • bieten Highlevel APIs und tiefe Integration in bestehende Methoden
    • Promise.all([promise1, promise2, ...aBunchOfPromises]);
    • Promise.race([hedgehogPromise, BunnyPromise]);
    • fetch('http://google.com').then(somehowSaveToDisk);
// Just for fun

const hedgehog = new Promise((resolve) => setTimeout(resolve, 500, 'hedgehog'));
const bunny    = new Promise((resolve) => setTimeout(resolve, 25, 'bunny ftw'));
const competitors = [hedgehog, bunny];

Promise.race(competitors)
    .then((winner) => console.log(winner));

Promise.all(competitors)
    .then((list) => console.log(`All ${list.length} competitors have arrived`));


// Production usecase

const bigData = fetch('http://wikipedia.com').then(response => response.blob());
const timeout = new Promise((resolve, reject) => {
    setTimeout(reject, 3000, new Error('Action took too long'));
});

Promise.race([bigData, timeout])
    .then(somehowConsume)
    .catch((reason) => console.error(reason));

Konkret

Promise.resolve('start')
    .then((step) => new Promise((resolve) => setTimeout(resolve, 500, 1)) )
    .then((step) => 2)
    .then((step) => {
        throw new Error(3);
    })
    .then((step) => 'Will not step in')
    .catch(() => 'In the place to be')
    .then((message) => console.log(message));

Einmal Promise, immer Promise

Default function parameters

function calculateBill (price, tax = 0.19, tip = 0.1) {
  return price + (price * tax) + (price * tip);
}

// Use the defaults
console.log(calculateBill(100)); // 129

// Specify custom values
console.log(calculateBill(100, 0.13, 0.15)); // 128

// Specify one of them 
console.log(calculateBill(100, undefined, 0.15)); // 134

Default values

// VS the ES5 way
function calculateBill (price, tax, tip) {
  tax = tax || 0.19;
  tip = tip || 0.1;

  return price + (price * tax) + (price * tip);
}
// Invocation 
selectEntries({ start: 0, end: -1 });

Passing object literals

// ES5 function declaration
function selectEntries(options) {
  var start = options.start || 0;
  var end = options.end || -1;
  var step = options.step || 1;
  // […]
}
// ES6 using destructuring in parameter definitions
function selectEntries({ start=0, end=-1, step=1 }) {
  // […]  
}

Making that optional

// ES5: Add line A 
function selectEntries(options) {
  options = options || {}; // (A)
  var start = options.start || 0;
  var end = options.end || -1;
  var step = options.step || 1;
  // […]
}
// ES6: specify {} as default value
function selectEntries({ start=0, end=-1, step=1 } = {}) {
  // […]
}

Fazit

  • Syntaktischer Zucker
  • Macht code deutlich
    • lesbarer
    • kompakter

Destructuring

  • Arrays und Iteratoren
  • Destrukturieren von Objekten
  • Emulation von Named Function Parameters
let [start, middle, end] = [1, 2, 3];
console.log({start, middle, end}); // → {start: 1, middle: 2, end: 3}


function makeIterator(obj) {
  let nextIndex = 0;
  const keys = Object.keys(obj);
  return {
    [Symbol.iterator]() {
      return {
        next: () => {
          if (nextIndex >= keys.length) return {done: true};
          const key = keys[nextIndex++];
          return {value: {key, value: obj[key]}, done: false};
      };
    }
  };
}

let [, secondTupel] = makeIterator({
  x: 100,
  y: 200,
  z: 300,
});

console.log(secondTupel); // → {key: "y", value: 200}

Arrays und Iteratoren

let person = {name: 'Kai', age: 30};

/**
 * Object destructuring
 */
let {name} = person;
console.log(name); // → Kai

/**
 * Object destructuring with mapped properties
 */
const {name: boss} = person;
console.log(boss); // → Kai

/**
 * Object destructuring in function parameters
 */
function drawRectangle({x: left = 0, y: top = 0, width = 100, height = width} = {}) {
  console.log('Somehow draw', {left, top, width, height});
}

// → "Somehow draw", {left: 0, top: 75, width: 100, height: 100}
drawRectangle({y: 75});

Objekte

For the ...rest of us

Rest Parameter

  • Fangen alle (ggf. übrigen) Parameter
  • Ist immer ein echtes Array
    • rest instanceof Array === true
  • ​Kann nur als letztes Argument auftauchen
/**
 * No brainer
 */
function join(...strings) {
    return strings.join(' ');
}

console.log(join('With', 'great', 'power')); // → "With great power"


/**
 * Semi brainer
 */
let [a, b, ...rest] = [1, 2, 3, 4, 5];

console.log({a, b, rest}); // → {a: 1, b: 2, rest: [3, 4, 5]}


/**
 * Brain cancer
 */
let [start, ...tail] = (function *() {for (let i = 0; i <= 100; i++) yield i})();

// → {start: 0, tail: [1, 2, 3, 4, 5, 6, 7, 8, 9, […], 100]}
console.log({start, tail});

Brain massage

let set = new Set([4, 5, 6, 5, 4]);
let biggerSet = [...set, 7 ,8, 9];
console.log(biggerSet); // -> [4, 5, 6, 7, 8, 9]

let alphabet = 'abcdefghijklmnopqrstuvwxyz';
let hugeSet = [0, 1, 2, 3].concat(biggerSet, ...alphabet);
console.log(hugeSet); // -> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", […],  "z"];

obj.method(...args) === obj.method.apply(obj, args);

...spread als Inverse zu ...rest

Classes

Syntaktischer Zucker für protype based inheritance

// ES5
function Person(name) {
  this.name = name;
}

Person.prototype.describe = function () {
  return 'Person called '+this.name;
};

var person = new Person("Michael");
console.log(person.describe());   // "Person called Michael"

Constructor functions

// ES6
class Person {
    constructor(name) {
        this.name = name;
    }
    describe() {
        return 'Person called '+this.name;
    }
}

let person = new Person("Michael");
console.log(person.describe());   // "Person called Michael"
// ES5
function Employee(name, title) {
  Person.call(this, name); // super(name)
  this.title = title;
}
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.describe = function () {
  return Person.prototype.describe.call(this) // super.describe()
         + ' (' + this.title + ')';
};

Inheritance – Derived Classes

// ES6
class Employee extends Person {
  constructor(name, title) {
    super(name);
    this.title = title;
  }
  describe() {
    return super.describe() + ' (' + this.title + ')';
  }
}

Neue OOP Features

neben classes

Object literals

Neue Features === syntaktischer Zucker

// We have some data
const name = 'Hansi';
const breed = 'Highlander';
const age = 5;

Property value shorthands

// and we need it in an object
const cat = {
  name: name,
  breed: breed,
  age: age
}
// With ES6 we can use property value shorthands
const cat = {
  name,
  breed,
  age,
  siblings: ['Emma', 'Minnie', 'Maxi']
}
// ES5 way to define methods
var modal = {
  open: function(content) {
    // open
  },
  close: function() {
    // close
  }
}

Method definitions

// The ES6 way
var modal = {
  open(content) {
    // open
  },
  close() {
    // close
  }
}

Neue Methoden

von object

/**
 * Object.assign(target, source_1, source_2, ···)
 * Merges the sources into the target
 */
const obj = { foo: 123 };
Object.assign(obj, { bar: true });

console.log(JSON.stringify(obj)); // {"foo":123,"bar":true}

Object.assign()

Object.assign()

// Another use case is adding methods to objects
Object.assign(SomeClass.prototype, {
  someMethod(arg1, arg2) {
    // […]
  },
  anotherMethod() {
    // […]
  }
});
// vs ES5 way
SomeClass.prototype.someMethod = function (arg1, arg2) {
  // […]
};
SomeClass.prototype.anotherMethod = function () {
  // […]
};

Object.assign()

// Use case: cloning objects
const myObject = {
  foo: 123,
  bar: true
}

function clone(original) {
  return Object.assign({}, original);
}

const myOtherObject = clone(myObject);
delete myObject.foo;

console.log(JSON.stringify(myObject)); // {"bar":true}
console.log(JSON.stringify(myOtherObject)); // {"foo":123,"bar":true}

Object.is()

// Will make JavaScript less weird

NaN === NaN; // false
Object.is(Nan, NaN); // true

0 === -0; // true
Object.is(0, -0); // false
// Everything else is compared as with strict equality operator
5 == '5'  // true
5 === '5' // false
Object.is(5, '5') // false

Modules

ES6 bringt ein Module-System 🎉

Zumindest theoretisch 🙈

… also, zur Theorie

Wofür Module?

  • Wartbarkeit
  • Das Problem mit dem globalen Scope
    • Pollution sorgt für Konfliktpotential

Lösungsansätze in ES5

// moduleFoo.js

// Namespace 
var myNamespace = window.myNamespace || {};

// Module (Revealing Module Pattern)
myNamespace.moduleFoo = (function ($) {
  'use strict';

  var yourPublicMethod = function (message) {
    console.info(message);
  };

  var _yourPrivateMethod = function (message) {
    console.info(message);
  };

  document.addEventListener('DOMContentLoaded', function () {
    _yourPrivateMethod('Hi Private.');
  });

  // Return functions to make them accessible from outside.
  return {
    yourPublicMethod: yourPublicMethod
  };

})();

Modules in ES5 mit Hausmitteln

// moduleBar.js

// Namespace 
var myNamespace = window.myNamespace || {};

// Module (Revealing Module Pattern)
myNamespace.moduleFoo = (function ($) {
  'use strict';

  var _anotherPrivateMethod = function (message) {
    console.info(message);
  };

  document.addEventListener('DOMContentLoaded', function () {
    _anotherPrivateMethod('Hi Private.');
    myNamespace.moduleFoo.yourPublicMethod('Hi public.');
  });
  
})();

ES6 modules

  • Wie CommonJS Module
    • Kein globaler scope
    • Weniger boilerplate🏼
// lib.js
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//foo.js
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

Multiple exports

//myFunc.js
export default function () { […] } // no semicolon!

// main1.js
import myFunc from 'myFunc';

myFunc();

Single exports

 ES6 modules verwenden

  • 💚 Module syntax ist in ES6 specs definiert
  • 💛 Die module loader specs sind 2015 aus ES6 raus 
    • Werden von der WHATWG weiterentwickelt 
  • 💔 Modules bis jetzt in keiner JS Engine implementiert

Module bundler 💝

  • lassen uns die ES6 module Syntax heute nutzen
  • transpilieren den code nach ES5
  • Große Auswahl an Tools wie:
    • browserify, webpack, jspm/SystemJS

Wo kann man ES6 nutzen?

Browsersupport

Browser ES6 Implementierung
Microsoft Edge 13 79%
Internet Explorer 11 15%
FireFox 46 90%
Chrome 50 93%
Safari 9 53%

Stand: 11. Mai 2016

Quelle: https://kangax.github.io/compat-table/es6/

Node.js Support

Version ES6 Implementierung
v6.1.0 96%
v5.x.x 58%
v4.x.x 47%
v0.12.14 30%

Stand: 11. Mai 2016

Quelle: http://node.green

😐

Transpiling to the rescue

ES6 ➡ ES5

Transpilersupport

Transpiler ES6 Implementierung
Closure compiler 42%
Traceur 58%
TypeScript 60%
Babel 74%

Stand: 11. Mai 2016

Quelle: https://kangax.github.io/compat-table/es6/

Wie kann ich Babel nutzen?

  • CLI
  • Build tool
    • Make, Broccoli, Brunch, Grunt, Gulp, …
  • Module bundler
    • Browserify, Rollup, Webpack

Babel ausprobieren

Teaser

ES6 in Bootstrap Kickstart

  • Nächstes Major Release steht kurz bevor
  • Wird folgendes mitbringen:
    • ES6 transpiling via Babel
    • Module bundling via browserify
    • Package management ausschließlich via npm
    • Automatisierte security checks (für dependencies) 
    • etc …
  • Siehe vor allem PR von Rene 😘

Links

Das war’s

Fragen, gerne und jederzeit.

ES6 / ES2015

By Michael Kühnel

ES6 / ES2015

– ECMAScript im Allgemeinen – ES6 Features – Wie kann ich ES6 heute nutzen

  • 2,540