Modern Javascript

Notions

  • Let, Var, Const
  • Templates literals (gabaris de libellés)
  • Objets
  • This et binding
  • Fonctions fléchées et this (arrow functions)
  • Fonctions de haut niveau (higher order functions)
  • Destructuring
  • Opérateur Spread (...)
  • Classes et héritage
  • Modules et exports

var
let
const

Le problème de var

Dans la majeure partie des langages, on s'attend à ce qu'une variable déclarée dans un bloc ne soit disponible que dans celui-ci

 

Ce n'est pas le cas d'une variable déclarée avec le mot clé var

for(var i = 0; i < 5; i++) {
    console.log(i);
}

console.log(i); // affiche 5

if(true) {
    var name = "Smith";
}

console.log(name); // affiche "Smith"

La solution : let

Depuis la version 6 du standard ECMAScript (ou ES2015), on peut utiliser un nouveau mot clé pour déclarer une variable de bloc 

 

Une variable déclarée avec le mot clé let n'est disponible que dans le bloc dans lequel elle a été déclarée !

for(let i = 0; i < 5; i++) {
    console.log(i);
}

console.log(i); // undefined

if(true) {
    let name = "Smith";
}

console.log(name); // undefined

La généralisation de const

Le standard ES2015 apporte aussi une généralisation du mot clé const permettant de déclarer une constante !

 

Les constantes ont aussi une portée limitée au bloc dans lequel elles sont déclarées !

 

On l'utilise de plus en plus souvent

const x = 1;

x = 2; // Erreur !! 
// Impossible de réassigner !

// Attention :
const personne = { 
    prenom: 'Lior', 
    nom: 'Chamla' 
};

personne.prenom = 'Elise'; // Possible

personne = 12; // Erreur ! 
// Impossible de réassigner !

Template literals

C'est une nouvelle façon d'écrire des strings. Grâce aux backticks (les accents grave), on déclare des chaines dans lesquelles on peut :

  • Aller à la ligne
  • Formater
  • Interpoler des valeurs

On interpole des valeurs grâce à la syntaxe : ${...}

const prenom = "Lior";
const nom = "Chamla";

const html = `
    <p>
        Dans cette syntaxe, on peut aller
        à la ligne et formater 
        comme on le souhaite !
    </p>
    <p>
        Au fait, je suis ${prenom} ${nom} !
    </p>
`;

Les objets

Javascript vous permet de regrouper des préoccupations similaires au sein d'objets (une personne, un requête HTTP, une facture ...)

 

Un objet est un ensemble de données (propriétés) et de comportements (méthodes)

const personne = {
    // propriétés
    prenom: 'Lior',
    nom: 'Chamla',

    // méthodes
    marcher() {}
    parler() {}
}

personne.marcher(); 

console.log(personne.prenom); 
// affiche 'Lior'

personne.prenom = 'Elise';
// modifie la propriété

Accès aux propriétés

On peut accéder aux données (propriétés) d'un objet par deux syntaxes différentes : objet.propriete ou objet["propriete"] !

const personne = {
    // propriétés
    prenom: 'Lior',
    nom: 'Chamla',

    // méthodes
    marcher() {}
    parler() {}
}

console.log(personne.prenom);
// affiche 'Lior'

console.log(personne['prenom']);
// affiche 'Lior'

const property = 'prenom';
console.log(personne[property]);
// affiche 'Lior'

La variable de contexte this

La variable this représente toujours le contexte d'exécution !

 

Au sein d'un objet, elle représente l'objet lui-même (bien pratique 😁)

 

En dehors, elle représente le contexte 

const personne = {
    prenom: 'Lior',
    nom: 'Chamla',

    parler() {
        console.log("Je suis " + this.prenom);
    }
}

personne.parler();
// affiche : "Je suis Lior"

console.log(this);
// représente l'objet global "window"

// this n'est pas le même en fonction
// du contexte dans lequel on fait appel
// à lui !

Le binding de this

Il est parfois utile, lorsqu'on définit une référence à une fonction de lui préciser ce que this doit représenter dans la fonction !

 

Dans le cas contraire, il représentera le contexte d'exécution

const personne = {
    prenom: 'Lior',
    nom: 'Chamla',

    parler() {
        console.log("Je suis " + this.prenom);
    }
}

personne.parler();
// affiche : "Je suis Lior"

button.addEventListener('click', personne.parler);
// affichera "Je suis undefined" 
// car this représentera le bouton

button.addEventListener(
    'click', 
    personne.parler.bind(personne)
);
// ici on précise qu'au sein de "parler"
// this représentera l'objet "personne"

Les fonctions fléchées (arrow functions)

Depuis la version 6 d'ECMAScript (ES2015), on peut utiliser une notation très élégante pour déclarer une fonction : la fonction fléchée !

 

Elle apporte bien plus que de l'élégance, un véritable avantage à voir ensuite ...

// Syntaxe classique
const additionner = function(x, y) {
    return x + y;
}

console.log(additionner(10, 10));
// affiche 20

// Fonction fléchée (arrow function)
const additionner = (x, y) => x + y;

console.log(additionner(10, 10));
// affiche 20

Expressivité accrue grâce aux fonctions fléchées

Un avantage qu'apporte cette nouvelle syntaxe est son expressivité en plus de son élégance.

 

 

const nombres = [10, 8, 9, 22, 30, 2, 50];

// Filtre avec syntaxe classique
// Assez "rugueux" à lire
const inferieursA10 = nombres.filter(function(nombre){ 
    return nombre < 10; 
});

// Filtre avec syntaxe fléchée
// Beaucoup plus expressif et clair
const inferieursA10 = nombres.filter(nombre => nombre < 10);
// Je ne prend que les nombres qui sont inférieurs à 10

Le véritable apport des fonctions fléchées : this

La fonction fléchée conserve le contexte d'exécution !

Elle permet de continuer à utiliser this au sein de fonctions de rappel (callback) !

 

La fonction fléchée ne recréé par de nouveau lien pour this !

const personne = {
    prenom: 'Lior',
    presenter() {
        setTimeout(function(){
            console.log('Je suis ' + this.prenom);
        }, 1000);
    },
    presenterArrow() {
        setTimeout(() => {
            console.log('Je suis ' + this.prenom);
        }, 1000);
    }
};

personne.presenter();
// affiche "Je suis undefined" après 1 seconde
// this ici ne fait pas référence à l'objet
// personne mais à la fonction dans laquelle
// il est appelé (contexte d'exécution global)

personne.presenterArrow();
// affiche "Je suis Lior" après 1 seconde
// this continue de représenter l'objet !

Les fonctions de haut niveau

Higher order functions

Higher Order Functions

Ce sont des fonctions qui nécessitent une autre fonction (callback) pour s'exécuter !

Particulièrement utiles sur les arrays (tableaux) !

 

  • filter (filtrage)
  • map (transformation)
  • reduce (réduction)
  • find & findIndex (recherche)
const nombres = [10, 20, 30, 40, 50];

// Filtrage :
const inferieursA30 = nombres.filter(function(nombre){
    return nombre < 30;
});

// Transformation :
const multipliesPar2 = nombres.map(function(nombre){
    return nombre * 2;
});

// Réduction :
const somme = nombres.reduce(function(total, nombre){
    return total + nombre;
}, 0);

// Recherche d'index :
const index = nombres.findIndex(function(nombre) {
    return nombre === 20;
});

Higher Order Functions

D'autres fonctions de haut niveau pour les tableaux :

 

  • some (contient)
  • every (contient uniquement)
  • sort (tri)
const nombres = [10, 20, 30, 40, 50];

// Contient 
const hasPair = nombres.some(function(nombre){
    return nombre % 2 === 0;
}); // boolean

// Tous
const hasOnlyPairs = nombre.every(function(nombre){
    return nombre % 2 === 0;
}); // boolean

// Tri
nombres.sort(function(a, b) {
    return a - b;
});

Destructuring d'objet

const personne = {
    prenom: 'Lior',
    nom: 'Chamla',
    ville: 'Marseille'
}

// A l'ancienne :
const prenom = personne.prenom;
const nom = personne.nom;

// Destructuring :
const { prenom, nom } = personne;

// Avec alias :
const { prenom: firstName, nom: lastName } = personne;

console.log(firstName); // affiche 'Lior'

Destructuring d'array

const personne = ['Lior', 'Chamla', 'Marseille'];

// A l'ancienne :
const prenom = personne[0];
const nom = personne[1];

// Destructuring :
const [ prenom, nom ] = personne;

console.log(prenom); // affiche 'Lior'

Spread !

Il permet d'éclater une structure telle qu'un tableau ou un objet !

const nombres = [10, 12, 2, 40];

// ...nombres 
// 10, 12, 2, 40
// Un peu comme si on enlevait les crochets donc
// Math.max(...nombres) équivaut à 
// Math.max(...[10, 12, 2, 40]) qui équivaut à
// Math.max(10, 12, 2, 40)

const lettres = ['a', 'b', 'c'];

const fusion = [...nombres, ...lettres];
// équivaut à [...[10, 12, 2, 40], ...['a','b','c']
// équivaut à [10, 12, 2, 40, 'a', 'b', 'c']


const personne = { prenom: 'Lior', nom: 'Chamla' };

// ...personne donne 
// prenom: 'Lior', nom: 'Chamla'

const personne2 = { ville: 'Marseille', ...personne };
// équivaut à { ville: 'Marseille', ...{prenom: 'Lior', nom: 'Chamla'} }
// équivaut à { ville: 'Marseille', prenom: 'Lior', nom: 'Chamla' }

Classes

ES6 apporte avec lui la notion de Classes (POO). Bien que ce ne soit pas un véritable système de classes (JS est un langage qui repose sur les prototypes), c'est une syntaxe qui rassure les programmeurs d'autres langages !

class Personne {
    type = "humain";

    constructor(nom, prenom) {
        this.nom = nom;
        this.prenom = prenom;
    }

    presenter() {
        console.log(`
            Je suis ${this.prenom} ${this.nom}
        `);
    }
}

const prof = new Personne('Chamla', 'Lior');
prof.presenter(); 
// affiche "Je suis Lior Chamla"

Héritage

Avec la notion de classes, ES6 apporte aussi la notion d'héritage !

 

Une classe qui hérite d'une autre classe bénéficie des comportements de celle-ci !

 

On utilise le mot clé extends pour indiquer qu'une classe hérite des comportements d'une autre !

class Banquier extends Personne {
    encaisser() {
        console.log('Veuillez payer !!');
    }
}

const banquier = new Banquier('Chamla', 'Lior');
// Le constructor appelé est celui 
// de la classe Personne !

banquier.encaisser();
// affiche "Veuillez payer !!"

banquier.presenter(); 
// affiche "Je suis Lior Chamla"
// C'est la méthode qui se trouve
// dans la classe Personne !

Modules ES6

On fait de l'import / export 📦🚢🤗 

Les modules 

ES6 apporte la notion de modules ! 

Elle permet d'améliorer la modularité du code en séparant le code en différents modules (fichiers)

 

Un module possède des données et comportements privés qu'il peut rendre public si il le souhaite

// fichier personne.js

class Personne {
    constructor(nom, prenom){
        this.nom = nom;
        this.prenom = prenom;
    }
    
    presenter() {
        console.log('Kikoo, je suis ' + this.prenom);
    }
}

// On rend la classe Personne disponible 
// depuis l'exterieur
export Personne;
// fichier app.js

// On importe la classe Personne du 
// fichier personne.js
import { Personne } from './personne'

const prof = new Personne('Chamla', 'Lior');

Les modules 

Une donnée ou une fonction qui n'est pas exportée par un module ne peut pas être importée ailleurs.

 

On peut se servir de cela pour protéger certaines informations et comportements, un peu comme l'encapsulation dans le paradigme objet

// fichier counter.js

const counter = 0;

export function increment() {
    counter++;
}

export function decrement() {
    counter--;
}

export function get() {
    return counter;
}
// fichier app.js

import { increment, decrement, get } from './counter';

increment(); // pas de soucis
get(); // pas de soucis, retourne 1

import { counter } from './counter';
// ERREUR : counter n'est pas un membre exporté
// dans le module ./counter !

Les modules 

Un module peut exporter un de ses membres par défaut !

 

 

// fichier counter.js

const counter = 0;

export function increment() {
    counter++;
}

export function decrement() {
    counter--;
}

export default function get() {
    return counter;
}
// fichier app.js

import get, { increment, decrement } from './counter';

increment(); // pas de soucis
get(); // pas de soucis, retourne 1

import { counter } from './counter';
// ERREUR : counter n'est pas un membre exporté
// dans le module ./counter !

ET VOILA !

Javascript ES6

By Lior CHAMLA

Javascript ES6

Découvrir les nouveautés de l'ES2015 (ES6) en JavaScript

  • 1,438