Mettez votre JavaScript à jour !
Jordane Grenat @JoGrenat

Historique
Abandon d'ECMAScript 4
Décembre 1999 : ECMAScript 3
Décembre 2009 : ECMAScript 5
Mi-2015 : ECMAScript 6
Mi-2016 : ECMAScript 7
...
5 stades
- Stade 0 : Strawman
 - 
Stade 1 : Proposal
 - 
Stade 2 : Draft
 - 
Stade 3 : Candidate
 - 
Stade 4 : Finished
 
Bienvenue dans
ES2015
Let et const
let color = 'blue';
// Error
let color = 'red';const color = 'red';
// Error
color = 'blue';
Merci de jeter vos var à la poubelle en sortant...
Objets
let firstName = 'Marcel';
let man = {
    firstName,
    getJob() {
        return this.job;
    },
    ['j' + 'o' + 'b']: 'Writer'
};Arrow functions
let numbers = [1, 2, 3, 4];
let squares = numbers.map((nb) => { 
    return nb * nb;
});
// squares = [1, 4, 9, 16]Syntaxe :
(arg1, arg2) => { instructions }
let numbers = [1, 2, 3, 4];
let squares = numbers.map(nb => { 
    return nb * nb;
});
// squares = [1, 4, 9, 16]Syntaxe :
arg1 => { instructions }
let numbers = [1, 2, 3, 4];
let squares = numbers.map(nb => nb * nb);
// squares = [1, 4, 9, 16]Syntaxe :
arg1 => instruction
Mais si on retourne un objet ?
let numbers = [ 1, 2, 3, 4 ];
let squares = numbers.map(nb => 
    { result: nb * nb }
);
// squares = [
//   undefined,
//   undefined,
//   undefined,
//   undefined
// ]On rajoute juste des parenthèses
let numbers = [1, 2, 3, 4];
let squares = numbers.map(nb => 
    ({ result: nb * nb })
);
// squares = [
//   1,
//   4,
//   9,
//   16
// ]let mathObject = {
  numbers: [ 1, 2, 3, 4 ],
  m: 3,
  getMultiples() {
    return this.numbers.map(nb => nb * this.m);
  }
};
// mathObject.getMultiples() = [ 3, 6, 9, 12 ]Important !
Le this est lié de façon lexicale !
Classes
Uniquement du sucre syntaxique
class Car {
    
    constructor(brand) {
        this.brand = brand;
    }
    
    displayBrand() {
        console.log('Brand: ' + this.brand);
    }
}
let ferrari = new Car('Ferrari'); // "new" is mandatory
ferrari.displayBrand(); // displays "Brand: Ferrari"// ES6
class Car {
    constructor(brand) {
        this.brand = brand;
    }
    
    displayBrand() {
        console.log('Brand: ' + this.brand);
    }
}// ES5
var Car = function(brand) {
    this.brand = brand;
};
Car.prototype.displayBrand = function() {
    console.log('Brand: ' + this.brand);
};// ES6
class Audi extends Car {
    constructor() {
        super('Audi');
    }
}// ES5
var Audi = function() {
    this.brand = 'Audi';
};
Audi.prototype = Object.create(Car.prototype);
Héritage
Attention, pas d'héritage multiple !
// ES6
class Car {
    constructor(brand) {
        this.brandName = brand;
    }
    set brand(brand) {
        this.brandName = brand;
    }
    get brand() {
        return this.brandName.toLowerCase();
    }
};
let car = new Car('Audi');
car.brand = 'ToYoTa';
console.log(car.brand); // "toyota"Getter / Setter
Getter / Setter
// ES5
var Car = function(brand) {
    var brandName = brand;
    Object.defineProperty(this, 'brand', {
        set: function(brand) { 
            brandName = brand.toUpperCase(); 
        },
        get: function() { 
            return brandName.toLowerCase(); 
        }
    });
};
var car = new Car('Audi');
this.brand = 'ToYoTa';
console.log(this.brand);// ES6
class Audi extends Car {
    static introduce() {
        console.log("Hi, I'm a car!");
    }
}
Audi.introduce(); // "Hi, I'm a car!"Méthodes statiques
// ES5
var Car = function() {};
Car.introduce = function() {
    console.log("Hi! I'm a car!");
};
Car.introduce(); // "Hi! I'm a car!"Destructuring
Permet de décomposer
des tableaux
let ranks = ['Marc', 'Laure', 'Patricia', 'Jérôme'];
let [first, second, third] = ranks;
console.log(first);   // "Marc"
console.log(second);  // "Laure"
console.log(third);   // "Patricia"let [value = 'Default value'] = [];
console.log(value);  // "Default value"Et des objets
let luc = {
    lastName: 'Pignon',
    age: 31
};
let {age: ageOfLuc} = luc;
console.log(ageOfLuc);   // 31
let {lastName} = luc; 
console.log(lastName);  // "Pignon"
let {birthYear = 1985} = luc;
console.log(age);   // 1985Très utile pour les
paramètres de fonction
function drawCircle({ x = 100, y = 100, r = 100 }) {
    
    // Draw a circle. 
    // If x, y or r are not specified, 
    // they have a default value
}Ou pour échanger
les valeurs de 2 variables
let a = 5;
let b = 3;
[b, a] = [a, b];Rest et spread
Opérateur
...
Utilisé en temps que "rest" = "tout le reste"
let ranks = ['Marc', 'Laure', 'Patricia', 'Jérôme'];
let [first, second, ...others] = ranks;
console.log(others);  // ['Patricia', 'Jérôme']
let luc = { lastName: 'Pignon', age: 31 };
let { lastName, ...others } = luc;Ne fonctionne pas avec les objets
NOK
Fonctionne aussi avec les
paramètres d'une fonction
function add(...args) {
    // args est un vrai tableau, 
    // contrairement à arguments
    return args.reduce((a, b) => a + b, 0);
}
console.log(add(1, 2, 6, 4)); // 13function add(a, b, c) {
    return a + b + c;
}
let numbers = [6, 4, 10];
console.log( add(...numbers) );Utilisé en temps que "spread"
Template literals
En Javascript, les chaînes complexes sont un cauchemar...
var name = "Marcel";
var string = "I am " + name + "\n" + 
    "and I have " + (3 + 5) + " apples\n" +
    "in my kitchen!";
// Or
var string = "I am " + name + " \n\
      and I have " + (3 + 5) + " apples \n\
      in my kitchen!";
ES6 introduit les templates literals
let name = "Marcel";
let string = `I am ${name} 
 and I have ${3 + 5} apples
 in my kitchen!`;Et les tags...
function tagFunction(strings, ...values) {
    console.log(strings[0]);  // "Hello "
    console.log(values[0]);   // "Marcel"
    console.log(strings[1]);  // "! I'm "
    console.log(values[1]);   // "Linda"
    console.log(strings[2]);  // ""
    
    return "replaced";
}
const you = "Marcel";
const me = "Linda";
let string = tagFunction`Hello ${you}! I'm ${me}`;
console.log(string); // "replaced"Ex: Internationalisation
const translations = {
    fr: {
        'Hello [0]! Date: [1]': 'Date : [1]. Bonjour [0] !'
    },
    de: {
        'Hello [0]! Date: [1]': 'Guten Tag [0]! Datum: [1]'
    }
};
let locale = 'fr';Ex: Internationalisation
function i18n([start, ...strings], ...values) {
    const localTranslations = translations[locale] || {};
    const key = strings.reduce((acc, string, i) 
        => `${acc}[${i}]${string}`, start);
    // Exemple: Hello [0]! Date: [1]
}
    return values.reduce((result, value, i) => {
        
    }, localTranslations[key] || key);
        return result.replace(`[${i}]`, value);        if (value instanceof Date) {
            value = value.toLocaleString(locale);
        }Ex: Internationalisation
const name = 'Rémi';
let french = i18n`Hello ${name}! Date: ${new Date()}`;
// "Date : 15/02/2017 à 20:20:46. Bonjour Rémi !"
locale = "de";
let german = i18n`Hello ${name}! Date: ${new Date()}`;
// "Guten Tag Rémi! Datum: 15.2.2017, 20:27:14"
Symbols
Un nouveau type primitif
let symbol = Symbol();Génère des ID uniques pouvant servir de clé pour des objets

Les clés sont des objets Symbol
Pas de conflits avec d'autres attributs
Ajout de nouvelles fonctionnalités
- Symbol.iterator
 - Symbol.match
 - Symbol.replace
 - Symbol.search
 - Symbol.split
 - Symbol.toStringTag
 - ...
 
Iteratérables et for...of
ES6 introduit la notion d'itérables
Un élément itérable implémente la fonction [Symbol.iterator]()
Cette fonction doit retourner un objet contenant une fonction next()
let myIterableObject = {
    [Symbol.iterator]() {
        return {
            next() {
                // code
            }
        }
    }
}Cette fonction next doit retourner un objet du type :
{
    value: "Value of the iteration",
    done: false
}On peut les itérer avec l'opérateur spread
let values = [...myIterableObject];On peut aussi utiliser la décomposition
let [first, second] = myIterableObject;Ou avec un for...of
for(let value of myIterableObject) {
    console.log(value);
}Que peut-on itérer ?
- Des chaines de caractères
 - Des tableaux
 - Ce qui implémente Symbol.iterator()
 - Des générateurs
 - Map / Set
 
Promises
Une promesse est un objet représentant une valeur et qui peut avoir trois états :
- En attente de résultat
 - Complétée avec succès
 - En erreur
 
Une promesse est utilisée pour faciliter et clarifier les appels asynchrones
// Let's say getFile is a function that 
// returns a promise
getFile(url).then(value => {
    // Code
}, error => {
    // Error code
});
getFile(url).catch(error => {
    // Error code
});Utiliser une promesse
Chaîner des promesses
getProfile(personId).then(profile => {
    return profile.name;
}).then(name => {
    // Code
});getFile(url).then(content => {
    throw new Error();
}).catch(error => {
    // Catch the error
});getUrlFromBDD(id).then(url => {
    return getFile(url):
}).then(content => {
    // Called after getFile completion
});Chaîner des promesses
function() {
 
   let promise = new Promise((resolve, reject) => {
    
        myAsynchronousOperation((err, value) => {
            if(err) {
                reject(err);
            } else {
                resolve(value);
            }
        });
    
    });
    
    return promise;
}Créer des promesses
// Resolved when all promises are resolved
Promise.all(iterable);
Promise API
// Resolved when one of the promises is resolved
Promise.race(iterable);
// Return a promise that is immediatly fulfilled
Promise.resolve(value); 
// Return a promise that is immediatly rejected
Promise.reject(error); Generators
function *countTo3() {
      yield 1;
      yield 2;
      yield 3;
}let iterator = countTo3();
console.log(iterator.next()); // Displays: { value: 1, done: false }
console.log(iterator.next()); // Displays: { value: 2, done: false }
console.log(iterator.next()); // Displays: { value: 3, done: false}
console.log(iterator.next()); 
// Displays: { value: undefined, done: true }function *countTo3() {
      yield 1;
      yield 2;
      return 3;
}
let it = countTo3();
it.next();
it.next();
console.log(it.next()); // Displays { value: 3, done: true }Example: Fibonacci
function* fibonacci() {
  let a = 0;
  let b = 1;
  yield 1;
  while(true) {
    [a, b] = [b, a + b];
    yield b;
  }
}
var g = fibonacci();
console.log(g.next().value);   // Displays: 1
console.log(g.next().value);   // Displays: 1
console.log(g.next().value);   // Displays: 2
console.log(g.next().value);   // Displays: 3
console.log(g.next().value);   // Displays: 5Un générateur est itérable
function* countTo3() {
    yield 1;
    yield 2;
    yield 3;
}
let values = [...countTo3()];
for(value of countTo3()) {
    console.log(value);
}function *sum() {
    let sum = 0;
    while(true) {
        sum += yield sum;
    }
}
let it = sum();
it.next();     // Initialize generator - can't accept input
it.next(3);    // Returns: 3
it.next(18);   // Returns: 21 it.next() peut prendre
un argument qui sera retourné par yield.
function *gen1() {
    yield 2;
    yield 3;
}Un générateur peut déléguer avec yield*
function *gen2() {
    yield 1;
    yield* gen1();
    yield 4;
}
let it = gen2();
it.next();    // Returns 1
it.next();    // Returns 2
it.next();    // Returns 3
it.next();    // Returns 4Exemple : parcours d'un arbre
// Source: http://www.2ality.com/2015/03/es6-generators.html
class BinaryTree {
    constructor(value, left=null, right=null) {
        this.value = value;
        this.left = left;
        this.right = right;
    }
    /** Prefix iteration */
    * [Symbol.iterator]() {
        yield this.value;
        if (this.left) {
            yield* this.left;
        }
        if (this.right) {
            yield* this.right;
        }
    }
}
Exemple : l'asynchrone avec les coroutines
getUrlFromBDD(id).then(url => {
    return getFile(url):
}).then(content => {
    console.log(content);
});co(function*(id) {
    const url = yield getUrlFromBDD(id);
    return yield getFile(url);
}).then(content => {
    console.log(content)
});npm install coDeux autres méthodes pour un générateur
- it.return() arrête le générateur
 - it.throw() envoie une erreur au générateur
 
it.return(value)
Effectue un return value
à la place du yield
it.throw(error)
Effectue un throw error
à la place du yield
function* myGenerator() {
    try {
        yield;
        yield;
    } catch(error) {
        console.log(`Error: ${error}`);
    } finally() {
        console.log("this is not the end");
        yield;
    }
}Des explications plus détaillées et de nombreux exemples
Defaults values
function add(a = 5, b = 5) {
    return a + b;
}
console.log(add());      // Display 10
console.log(add(3));     // Display 8
console.log(add(3, 4));  // Display 7Map / Set
WeakMap / WeakSet
Map
let map = new Map();
map
    .set('key1', 'value1')
    .set('key2', 'value2');
map.get('key1');  // Returns: 'value1'
map.size;         // Equals: 2
map.has('key1');  // Returns: true
map.delete('key1');
map.clear();
let map2 = new Map([['key1', 'value1'], ['key2', 'value2']]);Tout peut être une clé
map
    .set({}, 'value')
    .set(Symbol(), 'value')
    .set(34, 'value')
    .set(/regex/, 'value')
    .set(() => {}, 'value')
    .set(NaN, 'value')
    .set(true, 'value');Map est itérable
for(let [key, value] of map) {
    // Code
}
for(let value of map.values()) {
    // Code
}
for(let key of map.keys()) {
    // Code
}
map.forEach((value, key, collection) => {
    // Code
});WeakMap
- Une Map qui n'influe pas sur le garbage collector
 
- Non itérable
 
- get()
 - set()
 - has()
 - delete()
 
Exemple: Attributs privés
const PRIVATE_MEMBER = new WeakMap();
class MyClass {
    constructor() {
        PRIVATE_MEMBER.set(this, 'value');
    }
    displayPrivateMember() {
        console.log(PRIVATE_MEMBER.get(this));
    }
}Set
let set = new Set();
set
    .add('value1')
    .add('value2');
set.has('value1'); // Returns: true
set.size; // Equals: 2
set.delete('value1');
set.clear();
let set2 = new Set(['value1', 'value2']);Set est itérable
for(let value of set) {
    // Code
}
map.forEach((value, key, collection) => {
    // Code
});Pour uniformiser les interfaces de Set et Map, ces deux méthodes existent :
Dans le cas d'un Set, key = value
- set.entries()
 - set.keys()
 
WeakSet
- 
N'influe pas sur le garbage collector
 - N'est pas itérable
 
- add()
 - has()
 - delete()
 
Modules
Exports nommés
export function name1() {
};
export const name2 = 'value';
export let name3 = 1;
export class MyClass {};
let name4 = () => {};
let name5 = 5;
export { name4, name5 };
export { name4 as arrowFunc, name5 as five };Export par défaut
export default 'value';Importer
import theDefaultExport from 'lib';
import { name1, name2 } from 'lib';
import theDefaultExport, { name1 } from 'lib';
import { name1 as otherName } from 'lib';
import * as myLib from 'lib';
import 'lib';Nouveautés diverses
Identifiers
// Accents et autres caractères spéciaux
let réseauFrançais;
// Unicode
let \u0061;
let \u{61};
let 𐊧;
let ♥;
- 
Meilleure gestion de l'Unicode
 - 
Meilleure gestion du binaire
 - Meilleure gestion de l'octal
 
\u{20BB7}0b1111101110o767Nouveautés mathématiques
Number.EPSILON;
Number.isNaN(NaN);           // true
Number.isFinite(-Infinity);  // false
Number.parseInt('11', 8);    // 9
Number.parseFloat('11.3');   // 11.3
Nouveautés de String
'my string'.startsWith('my');   // true
'my string'.endsWith('ng');     // true
'my string'.includes('st');     // true
'hip'.repeat(3); // "hiphiphip"Nouveautés de Array
// Retourne un vrai tableau
Array.from(document.querySelectorAll('*'));
Array.of(1, 2, 3);
[1, 2, 3].fill(7, 1);            // [1,7,7]
[1, 2, 3].find(x => x == 3)      // 3
[1, 2, 3].findIndex(x => x == 3) // 2
[1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2]
[1, 2, 3].entries();
[1, 2, 3].keys();
[1, 2, 3].values();
Proxy
Les proxies permettent d'intercepter des opérations effectuées sur des objets
Ces opérations peuvent être :
- L'accès à une propriété en lecture
 - L'accès à une propriété en écriture
 - Appel de méthode
 - Suppression d'une propriété
 - La création d'une instance
 - ...
 
let proxy = new Proxy(target, handler);L'objet sur lequel on va intercepter
Notre objet intercepteur
const handler = {
    get(target, key, receiver) {
        return 'random_value';
    }
};
const emptyObject = {};
const proxy = new Proxy(emptyObject, handler);
console.log(proxy.randomProperty); // 'random_value'let {proxy, revoke} = Proxy.revocable(target, handler);
revoke();
console.log(proxy.test); // TypeError: RevokedProxy révocable
On passe à ES2016 ?
Array.prototype.includes
const names = ['Angélique', 'Jérôme', 'Luc'];
names.includes('Angélique');   // true~names.indexOf('Angélique');
names.indexOf('Angélique') !== -1;Exponentiation operator
const x = 2 ** 10;     // 1024const x = Math.pow(2, 10);Support
- ES2015 : kangax.github.io/compat-table/es6/
 - ES2016+ : kangax.github.io/compat-table/es2016plus/
 - Node : node.green
 
Un peu d'ES2017 ?
Async Functions
Une fonction async peut
utiliser le mot-clé 
await pour attendre le résultat d'une opération
Une fonction async renvoie une promesse
// ES7
async function loadFileContent(id) {
    let url = await getUrlFromBDD(id);
    return await getFile(url);
}
const loadFileContent = co.wrap(function*(id) {
    const url = yield getUrlFromBDD(id);
    return yield getFile(url);
});Shared memory
and atomics
const worker = new Worker('worker.js');
// To be shared
const sharedBuffer = new SharedArrayBuffer( // (A)
    10 * Int32Array.BYTES_PER_ELEMENT); // 10 elements
// Share sharedBuffer with the worker
worker.postMessage({sharedBuffer}); // clone
// Local only
const sharedArray = new Int32Array(sharedBuffer); // (B)// main.js
console.log('notifying...');
Atomics.store(sharedArray, 0, 123);
// worker.js
while (Atomics.load(sharedArray, 0) !== 123) ;
console.log('notified');En vrac
Objets
const luc = {
    name: 'Luc',
    age: 31
};
Object.values(luc); // ['Luc', 31]
Object.entries(luc); // [['name', 'Luc'], ['age', 31]]Padding
"test".padStart(10); // "      test"
"test".padEnd(10);   // "test      "Ca évitera de reposer sur left-pad...
Trailing comas
function sum(a, b,) {
    return a + b;
}
sum(3, 5,);
function sum(
    a, 
    b,
) {
    return a + b;
}
Et si on allait
plus loin encore ?
Performances
SIMD.JS
let var1 = new SIMD.Float32x4(1, 2, 3, 4);
let var2 = new SIMD.Float32x4(5, 6, 7, 8);
let var3 = SIMD.Float32x4.add(var1, var2); 
// float32x4[6,8,10,12]Simple Instruction, Multiple Data
=> Parallélisme
WebAssembly
Nouveau format compilé pour le web
Decorators
Une fonction qui agit sur une classe ou une propriété pour agir dessus et étendre son comportement
class Logger {
    
    @deprecated()
    warning() {
        // ...
    }
}const logger = new Logger();
logger.warning('Whatever');
// Displays in console : "'warning' is deprecated.function deprecated(target, name, descriptor) {
    descriptor.value = function(...args) {
        console.warn(`${name} is deprecated.`);
        descriptor.value.apply(this, args);
    };
    return descriptor;
}let description = {
  type: 'method',
  value: specifiedFunction,
  enumerable: false,
  configurable: true,
  writable: true
};class Logger {
    
    @deprecated('warn')
    warning() {
        // ...
    }
}const logger = new Logger();
logger.warning('Whatever');
// Displays in console : "'warning' is a deprecated method.
// You should consider using 'warn' instead.function deprecated(newMethod) {
    return (target, name, descriptor) => {
        descriptor.value = function(...args) {
            console.warn(`${name} is deprecated. 
You should consider using '${newMethod}.`);
            descriptor.value.apply(this, args);
        };
        return descriptor;
    }
}On peut décorer
- Classes
 - Propriétés
 - Méthodes
 - Getters / setters
 
Class properties
class MyClass {
    
    myProperty = 1;
    
    static myStaticProp = 42;
}Dynamic imports
import('./path/to/my/module.js')
    .then(module => {
      module.doSomething();
    })
    .catch(err => {
        console.error(err);
    });Observables
"Streams de données"
const observable = new Observable(observer => {
    
    let i = 1;
    const stopper = setInterval(() => observer.next(++i), 1000);
    return () => clearInterval(intervalId);
});Sur ce flux, on peut utiliser les fonctions habituelles de la programmation fonctionnelle
Map
Filter
ForEach
On peut souscrire à un Observable
const observer = {
    start(subscription) {},
    next(value) {},
    error(error) {},
    complete(complete) {}
};
const obs = observable.subscribe(observer);obs.unsubscribe();const observable = Observable.of(1, 2, 3);
const array123 = [1, 2, 3];
const observable2 = Observable.of(...array123);Observable.of()
const observable = Observable.from({
    [Symbol.observable](observer) {
        // ...
    }
});Observable.from()
const observable2 = Observable.from([1, 2, 3]);const observable = new Observable(observer => {
    document.addEventListener('click', observer.next, true);
    return () => { 
        document.removeEventListener(eventName, observer.next, true);
    };
});
observable
    .map(e => e.target)
    .forEach(::console.log);Exemple :
Écouter le DOM
Merci de votre attention
Et à vos claviers !
The future of Javascript
By ereold
The future of Javascript
Presentation about ES6 and the future of javascript today
- 2,350