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

try now

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