ECMAScript 2015
aka ES6
my favourite parts
do you have your laptop with you?
stop coding (or facebooking) and follow these slides at andreduarte.info
these slides contain code and you can play with it by clicking try me
Who am I?
André Duarte
Senior Front-end Developer
@ Blip - Paddy Power Betfair
Mostly building Betfair Sportsbook mobile web application
ECMAScript versions
1997
1998
1999
RIP
2009
2011
2015
2016
ES1
ES2
ES3
ES4
ES5
ES5.1
ES2015
ES2016
First versions. Mostly tried to solve the mess between Netscape's JavaScript and Microsoft's JScript.
We could have had modules, classes, everything!
But it was abandoned.
The stable versions.
"Strict mode" and so on.
Started by recovering a lot of the ES4 proposals.
Yearly iterations from now on.
ES2015 compatibility
0%
100%
Chrome 52 / Opera 39
97%
Node 6
92%
Edge 14
95%
Firefox 48
91%
Safari 9
54%
IE 11
11%
71%
71%
Babel
Babel
Syntax
let keyword
let a = 12
function myFunction() {
console.log(a) // 12
console.log(b) // Reference Error Exception
let b = 13
if (true) {
let c = 14
console.log(b) // 13
}
console.log(c) // Reference Error Exception
}
var a = 12
function myFunction() {
console.log(a) // 12
console.log(b) // undefined
var b = 13
if (true) {
var c = 14
console.log(b) // 13
}
console.log(c) // 14
}
Syntax
const keyword
const PI = 3.14
var r = 5
// 2 + 2 != 5
PI = 3.15 // Read only exception
var area = PI * r * r
console.log(area)
var PI = 3.14
var r = 5
// 2 + 2 = 5
PI = 3.15
var area = PI * r * r
console.log(area) // 78.75
Syntax
const !== immutable
const config = {
name: 'andré'
}
config.name = 'el pixel'
console.log(config.name) // el pixel
Object.freeze(config)
config.name = 'andré'
console.log(config.name) // still el pixel
Syntax
default parameters
function myFunction (x = 1, y = 2, z = 1 + 2) {
console.log(x, y, z)
}
myFunction(6, 7) // 6 7 3
function myFunction (x, y, z) {
x = x === undefined ? 1 : x
y = y === undefined ? 2 : y
z = z === undefined ? 3 : z
console.log(x, y, z)
}
myFunction(6, 7) // 6 7 3
Syntax
rest parameter
function myFunction (a, b) {
}
myFunction(1, 2, 3, 4, 5) // 3, 4, 5
function myFunction (a, b, ...extraArgs) {
console.log(extraArgs)
}
// note: myFunction.length returns the number of arguments expected by the function
// note: arguments is an array-like object, but not really an array,
// so don't expect it to inherit functions from Array.prototype
// such as Array.slice()
var extraArgs = Array.prototype.slice.call(arguments, myFunction.length)
console.log(extraArgs)
Syntax
Template Literals
let str1 = "Hello world!"
let str2 = `Hello world!`
console.log(str1 === str2) //true
let str3 = "'Hello\nMy name \"is\" André', he said."
let str4 = `'Hello
My name "is" André', he said.`
// The only character that needs escaping is the ` itself
let event = 'Pixels Camp'
let today = new Date()
let str5 = "I'm loving " + event + ' ' + today.getFullYear() + "!"
let str6 = `I'm loving ${event} ${today.getFullYear()}!`
// I'm loving Pixels Camp 2016!
Syntax
Enhanced Object literals
const someConstant = 3.14
let name = 'john'
let surname = 'smith'
let oldObject = {
someConstant: someConstant,
name: name,
surname: surname,
saySomething: function saySomething() {
console.log(`Hello ${this.name} ${surname}!`)
}
}
oldObject.saySomething()
const someConstant = 3.14
let name = 'john'
let surname = 'smith'
let newObject = {
someConstant,
name,
surname,
saySomething () {
console.log(`Hello ${this.name} ${surname}!`)
}
}
newObject.saySomething()
Syntax
object destructuring
let presentation = {
title: 'ES2015',
duration: '45min'
}
let title = presentation.title
let duration = presentation.duration
let {title, duration} = presentation
let {title: foo, duration: bar} = presentation
console.log(foo) // ES2015
console.log(bar) // 45min
Syntax
object destructuring
function tweetThis(message, optionalConfigs) {
let when = optionalConfigs.when || Date.now()
let shareToFacebook = optionalConfigs.shareToFacebook || false
// ...
}
function newTweetThis(message, {when, shareToFacebook}) {
when = when || Date.now()
shareToFacebook = shareToFacebook || false
// ...
}
function es6TweetThis(message, {when = Date.now(), shareToFacebook = false}) {
// Just code!
}
Syntax
(arrow) => functions
function circleArea(pi, r) {
return pi * r * r
}
let a = ['a', 'ab', 'abc', 'abcd']
let aCount = a.map(function (input) {return input.length})
console.log(aCount) // 1, 2, 3, 4
let circleAreaShort = (pi, r) => {
return pi * r * r
}
let circleAreaShorter = (pi, r) => pi * r * r
conosle.log(circleArea(3.14, 3), circleAreaShort(3.14, 3), circleAreaShorter(3.14, 3))
// 28.26, 28.26, 28.26
let bCount = a.map(input => input.length)
console.log(bCount) // 1, 2, 3, 4
Syntax
(arrow) => functions
function TestModule() {
'use strict';
var self = this;
this.words = ['i', 'love', 'pixels', 'camp'];
this.sentence = '';
this.betterSentence = '';
}
this.buildBetterSentence = function () {
this.words.forEach((word) => {
this.betterSentence += word + ' ';
});
};
💩
💩
this.buildSentence = function () {
this.words.forEach(function (word) {
// this would be undefined
self.sentence += word + ' ';
});
};
Classes
class Person {
}
let student = new Student('john')
student.printName()
Student.sayHello(student)
function Person(name) {
this.name = name
}
Person.prototype.printName = function () {
console.log(this.name)
}
Person.sayHello = function (person) {
console.log(`Hello ${person.name}!`)
}
let student = new Person('André')
student.printName()
Student.sayHello(student)
constructor(name) {
this.name = name
}
printName() {
console.log(this.name)
}
static sayHello(student) {
console.log(`Hello ${person.name}!`)
}
Classes
class Person {
constructor(name) {
this.name = name
}
printName() {
console.log(this.name)
}
}
function Person(name) {
this.name = name
}
Person.prototype.printName = function () {
console.log(this.name)
}
Person.call(this, name)
function Student(name, school) {
this.school = school
}
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student
Student.prototype.printSchool = function () {
console.log(this.school)
}
let student = new Student('André', 'FEUP')
student.printName()
student.printSchool()
class Student extends Person {
}
constructor(name, school) {
super(name)
this.school = school
}
printSchool() {
console.log(this.school)
}
Modular Programming
const apiURL = 'https://api.discogs.com'
const daftPunkId = 1289
const myCurrency = 'EUR'
function fetchReleasesFromArtist(artistId) {
return fetch(`${apiURL}/artists/${artistId}/releases?sort=year`)
.then(res => res.json()).then(res => res.releases)
}
function fetchReleaseDetails(releaseId) {
return fetch(`${apiURL}/releases/${releaseId}?curr_abbr=${myCurrency}`)
.then(res => res.json())
}
fetchReleasesFromArtist(daftPunkId)
.then(releases => fetchReleaseDetails(releases[0].id))
.then(release => {
console.log(`You can get ${release.title}
for €${Math.round(release.lowest_price, 2)}.`)
})
constants.js
services.js
core.js
Modular Programming
const apiURL = 'https://api.discogs.com'
const daftPunkId = 1289
const myCurrency = 'EUR'
function fetchReleasesFromArtist(artistId) {
return fetch(`${apiURL}/artists/${artistId}/releases?sort=year`)
.then(res => res.json()).then(res => res.releases)
}
function fetchReleaseDetails(releaseId) {
return fetch(`${apiURL}/releases/${releaseId}?curr_abbr=${myCurrency}`)
.then(res => res.json())
}
fetchReleasesFromArtist(daftPunkId)
.then(releases => fetchReleaseDetails(releases[0].id))
.then(release => {
console.log(`You can get ${release.title}
for €${Math.round(release.lowest_price, 2)}.`)
})
bundle.js
function () {
}())
Modular Programming
const apiURL = 'https://api.discogs.com'
const daftPunkId = 1289
const myCurrency = 'EUR'
function fetchReleasesFromArtist(artistId) {
return fetch(`${apiURL}/artists/${artistId}/releases?sort=year`)
.then(res => res.json()).then(res => res.releases)
}
function fetchReleaseDetails(releaseId) {
return fetch(`${apiURL}/releases/${releaseId}?curr_abbr=${myCurrency}`)
.then(res => res.json())
}
fetchReleasesFromArtist(daftPunkId)
.then(releases => fetchReleaseDetails(releases[0].id))
.then(release => {
console.log(`You can get ${release.title}
for €${Math.round(release.lowest_price, 2)}.`)
})
constants.js
services.js
core.js
Modular Programming
export const apiURL = 'https://api.discogs.com'
export const daftPunkId = 1289
export const myCurrency = 'EUR'
import {apiURL, myCurrency} from "./constants"
function fetchReleasesFromArtist(artistId) {
return fetch(`${apiURL}/artists/${artistId}/releases?sort=year`)
.then(res => res.json()).then(res => res.releases)
}
function fetchReleaseDetails(releaseId) {
return fetch(`${apiURL}/releases/${releaseId}?curr_abbr=${myCurrency}`)
.then(res => res.json())
}
import {daftPunkId} from "./constants"
import api from "./services"
api.fetchReleasesFromArtist(daftPunkId)
.then(releases => api.fetchReleaseDetails(releases[0].id))
.then(release => {
console.log(`You can get ${release.title}
for €${Math.round(release.lowest_price, 2)}.`)
})
constants.js
services.js
core.js
const apiURL = 'https://api.discogs.com'
const daftPunkId = 1289
const myCurrency = 'EUR'
function fetchReleasesFromArtist(artistId) {
return fetch(`${apiURL}/artists/${artistId}/releases?sort=year`)
.then(res => res.json()).then(res => res.releases)
}
function fetchReleaseDetails(releaseId) {
return fetch(`${apiURL}/releases/${releaseId}?curr_abbr=${myCurrency}`)
.then(res => res.json())
}
fetchReleasesFromArtist(daftPunkId)
.then(releases => fetchReleaseDetails(releases[0].id))
.then(release => {
console.log(`You can get ${release.title}
for €${Math.round(release.lowest_price, 2)}.`)
})
export default {fetchReleasesFromArtist, fetchReleaseDetails}
ES 2016
What's next?
not much...
ES 2016
Math.pow(2, 3) // 8
exponential operator
let newPow = 2 ** 3
console.log(newPow) // 8
Array.prototype.includes
let arr = ['a', 'b', 'c']
let hasA = arr.indexOf('a') !== -1
let hasB = arr.includes('b') // true
let hasD = arr.includes('d') // false
ES next
// use fetch as a promise-based alternative to xhr
fetch('https://api.discogs.com/artists/1289/releases?sort=year&sort_order=desc')
.then(res => res.json())
.then(res => res.releases[0].id)
.then(releaseId => fetch(`https://api.discogs.com/releases/${releaseId}?curr_abbr=EUR`))
.then(res => res.json())
.then(release => {
console.log(`You can get ${release.title} for €${Math.round(release.lowest_price, 2)}.`)
})
async/await functions
ES 2017 / 2018 / etc.
fs.readdir(source, function (err, files) {
if (err) {
console.log('Error finding files: ' + err)
} else {
files.forEach(function (filename, fileIndex) {
console.log(filename)
gm(source + filename).size(function (err, values) {
if (err) {
console.log('Error identifying file size: ' + err)
} else {
console.log(filename + ' : ' + values)
aspect = (values.width / values.height)
widths.forEach(function (width, widthIndex) {
height = Math.round(width / aspect)
console.log('resizing ' + filename + 'to ' + height + 'x' + height)
this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
if (err) console.log('Error writing file: ' + err)
})
}.bind(this))
}
})
})
}
}) // Welcome to callback hell
(async function getCheapestRecord() {
let fetchReleases = await fetch('https://api.discogs.com/artists/1289/releases?sort=year')
let releasesJson = await fetchReleases.json()
let releaseId = releasesJson.releases[0].id
let fetchDetails = await fetch(`https://api.discogs.com/releases/${releaseId}`)
let detailsJson = await fetchDetails.json()
console.log(`You can get ${detailsJson.title}
for €${Math.round(detailsJson.lowest_price, 2)}.`)
}());
already available on Chrome Canary and Babel
Thank you!
Tweet your questions to @onemanclapping
Presentation available at http://andreduarte.info
Promises
fs.readdir(source, function (err, files) {
if (err) {
console.log('Error finding files: ' + err)
} else {
files.forEach(function (filename, fileIndex) {
console.log(filename)
gm(source + filename).size(function (err, values) {
if (err) {
console.log('Error identifying file size: ' + err)
} else {
console.log(filename + ' : ' + values)
aspect = (values.width / values.height)
widths.forEach(function (width, widthIndex) {
height = Math.round(width / aspect)
console.log('resizing ' + filename + 'to ' + height + 'x' + height)
this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
if (err) console.log('Error writing file: ' + err)
})
}.bind(this))
}
})
})
}
}) // Welcome to callback hell
Promises
new Promise( /* executor */ function(resolve, reject) { ... } )
A Promise
represents a value which may be available now, or in the future, or never.
Promise.prototype.then(function onFulfilled(value) { ... })
Promise.prototype.catch(function onRejected(reason) { ... })
Promise.all(iterable<Promise>).then(function allFulfilled(values) { ... })
Promise.race(iterable<Promise>).then(function firstFulfilled(value) { ... })
Executes the callback as soon as the promise is fulfilled.
Executes the callback if anything goes wrong.
Returns a promise that resolves when all promises are fulfilled.
Returns a promise that resolves as soon as one promise is fulfilled.
Promises
// use fetch as a promise-based alternative to xhr
fetch('https://api.discogs.com/artists/1289/releases?sort=year&sort_order=desc')
.then(res => res.json())
.then(res => res.releases[0].id)
.then(releaseId => fetch(`https://api.discogs.com/releases/${releaseId}?curr_abbr=EUR`))
.then(res => res.json())
.then(release => {
console.log(`You can get ${release.title} for €${Math.round(release.lowest_price, 2)}.`)
})
Promises
function waitAndPrint(msg) {
return new Promise(resolve => {
setTimeout(() => {
console.log(msg)
resolve()
}, 3000)
})
}
function fetchSomeData() {
let url = 'https://api.discogs.com/artists/1289/releases?sort=year&sort_order=desc'
return fetch(url).then(res => res.json())
}
Promise.all([fetchSomeData(), fetchSomeData()])
.then(data => {
console.log('Data is here. Comparing content.')
let isDataEqual = data[0].releases[0].id === data[1].releases[0].id
return isDataEqual
})
.then(waitAndPrint)
.then(() => {
console.log('Done!')
})
.catch(e => {
console.log('Something went wrong', e)
})
Built-in objects
Set() & Map()
let set = new Set('Hello!!!')
console.log(set)
// [object Set] { ... }
console.log(...set)
// That's the spread operator ...
// H, e, l, o, !
console.log(set.has('e')) // true
console.log(set.size) // 5
set.add({})
set.delete('o')
let map = new Map()
let o = {n: 1}
map.set(o, 'object')
map.set(123, 'number')
console.log(map.has(123)) // true
console.log(map.get(o)) // 'object'
map.delete(123)
console.log(...map)
// [{n: 1}, 'object']
Built-in objects
Iterator symbol (for...of)
let array = [10, 20, 30]
for (let value of array) {
console.log(value)
}
let string = "boo"
for (let value of string) {
console.log(value)
}
let divs = document.querySelectorAll("div");
for (let value of divs) {
console.log(value.textContent)
}
Built-in objects
Symbols
let s1 = Symbol()
let s2 = Symbol()
console.log(s1 === s2) // false
console.log(typeof s1) // 'symbol'
let obj = {
[s1]: 'foo'
}
console.log(obj[s1]) // 'foo'
obj[Symbol.for('name')] = 'special name'
console.log(obj.name) // undefined
console.log(obj[Symbol.for('name')]) // 'special name'
Iterator symbol (for...of)
let object = {
firstClassData: [1, 2],
secondClassData: [3, 4],
}
for (let value of object) {
console.log(value) // 1, 2, 3, 4
}
[Symbol.iterator]: function () {
let bigArray = this.firstClassData.concat(this.secondClassData)
let nextIndex = 0
return {
next() {
if (nextIndex < bigArray.length) {
return {
value: bigArray[nextIndex++],
done: false
}
} else {
return {
done: true
}
}
}
}
}
Syntax
object destructuring
let pres = someService.getCurrentPresentation()
{
title: 'ES2015',
duration: '45min',
speaker: {
name: 'André Duarte',
handlers: {
twitter: 'onemanclapping',
github: 'onemanclapping'
}
}
}
let {
title: theTitle,
speaker: {
handlers: {
twitter: theTwitter
}
}
} = pres
console.log(`@${theTwitter} is presenting ${theTitle}.`)
// @onemanclapping is presenting ES2015.
ES2015: my favourite parts
By André Duarte
ES2015: my favourite parts
- 895