Michael Kühnel
Frontend Developer working for Micromata 👨🏼💻
and beyond …
and beyond …
Näheres zur Historie:
Eine kurze Geschichte von JavaScript
// 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
const company = 'Micromata';
// 42 locs later
company = 'Polyas'; // → TypeError: 'company' has already been declared
const vinnie = {
age: 0,
weight: 3.5
}
// Properties can change
vinnie.age = 1;
vinnie.weight = 12;
console.log(vinnie); // → Object {age: 1, weight: 12}
// 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.
// 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.
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);
// 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
// Use existing functions
const bill = `your total bill is ${calculateBill(11.54)} including taxes.`
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']
// Functions in ES5
var names = ['Hans', 'Emma'];
var fullNames = names.map(function(name){
return `${name} Eichenfeld`;
});
console.log(fullNames); // ["Hans Eichenfeld", "Emma Eichenfeld"]
// 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`;
});
// 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 an object literal
var fullNames = names.map(name => ({first: name, last: 'Eichenfeld'}) );
var fullNames = names.map(function(name){
return `${name} Eichenfeld`;
});
const numbered = names.map((name, index) => `${name} is No. ${index + 1}`);
setTimeout(() => { console.log('done') }, 1000);
// vs ES5 way
setTimeout(function () {
console.log('done');
}, 1000);
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);
});
// 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);
});
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));
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));
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
// 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 });
// 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 }) {
// […]
}
// 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 } = {}) {
// […]
}
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}
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});
rest instanceof Array === true
/**
* 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});
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);
// 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"
// 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 + ')';
};
// ES6
class Employee extends Person {
constructor(name, title) {
super(name);
this.title = title;
}
describe() {
return super.describe() + ' (' + this.title + ')';
}
}
// We have some data
const name = 'Hansi';
const breed = 'Highlander';
const age = 5;
// 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
}
}
// The ES6 way
var modal = {
open(content) {
// open
},
close() {
// close
}
}
/**
* 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}
// 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 () {
// […]
};
// 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}
// 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
// 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
};
})();
// 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.');
});
})();
// 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
//myFunc.js
export default function () { […] } // no semicolon!
// main1.js
import myFunc from 'myFunc';
myFunc();
Browser | ES6 Implementierung |
---|---|
Microsoft Edge 13 | 79% |
Internet Explorer 11 | 15% |
FireFox 46 | 90% |
Chrome 50 | 93% |
Safari 9 | 53% |
Stand: 11. Mai 2016
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
😐
Transpiler | ES6 Implementierung |
---|---|
Closure compiler | 42% |
Traceur | 58% |
TypeScript | 60% |
Babel | 74% |
Stand: 11. Mai 2016
Siehe: babeljs.io/docs/setup
Siehe: babeljs.io/repl
By Michael Kühnel
– ECMAScript im Allgemeinen – ES6 Features – Wie kann ich ES6 heute nutzen