La syntaxe

Avant de s’attaquer au cœur du sujet, il est primordial de voir quelques bases sur la syntaxe JavaScript moderne.

Voyons donc ensemble les nouveautés en JS, et ce qu’elles peuvent nous apporter pour un code plus simple et moderne.

Si vous avez un doute sur le support d’une fonctionnalité (puisqu’il s’agit ici de choses assez récentes), le site Can I use permet de voir rapidement les navigateurs qui la supportent ou non.

Les variables et constantes

Si vous avez déjà fait un peu de développement, vous êtes probablement déjà familiers avec le concept des variables.

Pour faire simple, ce sont des noms que vous utilisez pour stocker des données (qu’il s’agisse de chaînes de caractère, de nombres, de booléens ou même d’objets complexes).

var pour les variables à grande portée

Historiquement, en JavaScript on utilisait le mot-clé var pour déclarer une variable :

var bonjour // Déclaration de la variable
bonjour = 'Bonjour tout le monde' // Initialisation de la variable

var age = 42 // Déclaration et initialisation en une seule instruction

Le problème de ces variables est leur portée : elles sont valides pour l’ensemble de la fonction qui les contient. Il devient donc difficile de les déclarer dans un bloc qui n’est pas une fonction, comme une boucle :

for (var i=1; i<=10; i++) {
	var j = i // 1, 2, 3 … 10
}
j // 10
i // 11

On le voit ici : la variable j a été déclarée dans la boucle, mais est accessible à l’extérieur et risque par exemple de rentrer en conflit avec une autre variable j.

Pour cette raison, il existe maintenant un mot-clé plus pratique qui remplace peu à peu var

let pour les variables locales

Tout comme var, le mot-clé let permet de déclarer une variable. La différence est sa portée, qui est limitée au bloc qui la contient :

for (let i=1; i<=10; i++) {
	let j = i // 1, 2, 3 … 10
}
j // undefined
i // undefined

Les variables ne sont ainsi accessibles que dans le bloc (ici délimité par des accolades) qui les contient (ainsi que leurs enfants), sans écraser des variables d’un bloc supérieur.

const pour les valeurs fixes

Et si vous voulez enregistrer une valeur qui ne changera jamais ? Vous savez, une constante ! 😉

Et bien, il existe le mot-clé const qui fonctionne à peu près comme let, sauf qu’il ne vous laisse pas modifier la valeur vers laquelle le nom pointe.

const AGE_MINIMUM = 7
const AGE_MAXIMUM = 77

AGE_MINIMUM-- // Invalide, la valeur ne peut pas être remplacée
AGE_MAXIMUM = 100 // Invalide, la valeur ne peut pas être remplacée

Attention : vous ne pouvez pas modifier le pointeur (la référence mémoire), mais vous pouvez modifier l’objet contenu dans votre constante :

const ANIMAUX = ['🐬', '🐺', '🦊']
ANIMAUX.push('🦙', '🦌') // 5 
// Le tableau contient maintenant 5 éléments sans erreur, car il s'agit d'un objet et non d'une valeur primaire

Si vous voulez en apprendre plus sur les déclarations en JS, n’hésitez pas à lire la page MDN sur le sujet.

Les littéraux de gabarits

Également appelés templates literals pour les intimes, il s’agit d’une nouvelle façon de créer des chaînes de caractère, avec plusieurs avantages à la clé…

Les sauts de ligne

À la différence des chaînes classiques, délimitées par des quotes (simples ou doubles), vous pouvez sauter une ligne sans problème dans un littéral de gabarit :

let singleQuotes = '
' // SyntaxError: '' string literal contains an unescaped line break

let doubleQuotes = "
" // SyntaxError: "" string literal contains an unescaped line break

let templateLiteral = `
` // OK

L’interpolation de données

Fini les concaténations sans fin ! Si vous voulez injecter une variable (ou une constante) dans un littéral de gabarit, il existe une syntaxe spécifique pour ne pas avoir à concaténer, en utilisant le marqueur ${} autour de votre valeur :

const NUMERO = '007'

let message = `Bonjour, Agent ${NUMERO}` // "Bonjour, Agent 007"

Et ça fonctionne aussi avec une expression, un appel de fonction ou une propriété d’objet :

const auteur = {
	pseudo: 'viki53',
	espece: '🐬',
	tutoriels: 4
}

function nomEspece(code) {
	switch (code) {
		case '🐬':
			return 'un dauphin'
		case '🐺':
			return 'un loup'
		case '🦌':
			return 'un caribou'
		case '🦊':
			return 'un renard'
		default:
			return 'un animal inconnu'
	}
}

let presentation = `Ceci  est le ${auteur.tutoriels + 1}ᵉ tutoriel de ${auteur.pseudo}, qui est ${nomEspece(auteur.espece)}` // "Ceci  est le 5ᵉ tutoriel de viki53, qui est un dauphin"

Les valeurs sont évaluées au moment de la déclaration, pas à chaque fois que vous faites appel à votre chaîne. Si vous changez une valeur après avoir déclaré la chaîne il faudra la redéfinir.

let langage = 'JavaScript'

let message = `Le meilleur langage pour le Web est ${langage}`

langage = 'Python'

message // "Le meilleur langage pour le Web est le JavaScript"

Les étiquettes ou tags

En plus de pouvoir interpoler des variables, vous pouvez aussi récupérer chaque morceau des chaînes pour modifier la chaîne finale via une étiquette :

const nom = 'viki53'
const metier = 'développeur'

Ces étiquettes sont des fonctions, qui doivent répondre à une définition précise : le premier paramètre est un Array contenant les morceaux bruts de la chaîne (en dehors des marqueurs d’interpolation), les autres paramètres contenant les valeurs à injecter :

function majuscules(morceaux, ...valeurs) {
	let str = ''

	for (let i in morceaux) {
		str += morceaux[i] + (valeurs[i] || '').toUpperCase()
	}

	return str
}

On peut ensuite appliquer le tag à notre template string :

const direBonjour = majuscules`Bonjour, je m'appelle ${nom} et je suis ${metier}` // "Bonjour, je m'appelle VIKI53 et je suis DÉVELOPPEUR"

Les fonctions fléchées

Les arrow functions sont des fonctions comme les autres, à un détail près : elles n’ont pas de this spécifique. Elles permettent également une syntaxe plus courte, pratique pour déclarer des fonctions anonymes.

function classique() {
	console.dir(this) // body
}
document.body.addEventListener('click', classique)

const flechee = () => {
	console.dir(this) // Window
}
document.body.addEventListener('click', flechee)

Si vous avez un doute sur l’usage de this ou ce qu’il définit, vous pouvez trouver une explication sur le MDN.

Ça peut paraître abstrait pour le moment, mais vous verrez en construisant l’application que c’est très utile ^^

À noter que les fonctions (fléchées ou non) n’ont pas forcément besoin d’un nom, elles peuvent être anonymes :

document.documentElement.addEventListener('scroll', event => { console.dir(event) }, { once: true, passive: true })

Les modules

Une des nouveautés du JavaScript moderne est l’apparition des modules, qui permettent de structurer son code en séparant chaque fonctionnalité ou composant indépendamment du reste : chaque module gère son aspect métier sans se soucier des autres.

Chaque module est exécuté dans un contexte différent : vous ne risquez donc pas d’avoir des conflits de variables entre deux fichiers. Un module peut donc, en règle générale, être transposé d’un projet à l’autre directement sans risque.

Avant de pouvoir charger un module, il faut d’abord en exporter un pour le rendre accessible aux autres :

// Cette fonction sera chargée par défaut si l'import ne précise pas d'importer autre chose
export default function maFonction() {
	console.info('Bonjour !')
}
ma-fonction.js

L’export permet d’exposer une valeur afin que les autres modules puissent y accéder. Sans export la valeur reste interne au module, les autres ne peuvent donc pas y accéder directement.

Vous pouvez également exporter un ensemble de variables et fonctions :

export const auteur = {
	pseudo: 'viki53',
	espece: '🐬'
}

export const meilleureEspece = '🐬'

export function nomEspece(code) {
    switch (code) {
        case '🐬':
            return 'un dauphin'
        case '🐺':
            return 'un loup'
        case '🦌':
            return 'un caribou'
        case '🦊':
            return 'un renard'
        default:
            return 'un animal inconnu'
    }
}
un-module.js

Pour utiliser un module, il faut déclarer au navigateur que l’on veut charger notre fichier dans ce contexte, via un attribut HTML :

<script src="./js/mon-app.js" type="module"></script>

Notre JavaScript pourra alors à son tour charger d’autres modules, via une syntaxe spécifique :

import maFonction from './ma-fonction.js' // Charge l'export par défaut (`default`) du module sous le nom `maFonction`
import * as monModule from './un-module.js' // Charge l'ensemble du module
import { auteur, nomEspece as emojiVersNomEspece } from './un-module.js' // Importe une partie du module, renomme un des imports

À noter que les chemins pour les imports sont calculés à partir du dossier courant.

Les class

Pour se rapprocher d’autres langages, le JS moderne a introduit les class pour définir des objets, avec des propriétés et des méthodes selon une syntaxe classique :

class Personne {
	constructor(nom) {
		this.nom = nom
	}

	direBonjour() {
		return `Bonjour, je m'appelle ${this.nom}`
	}
}
Attention

Il ne s’agit pas réellement d’objets, mais de prototypes (la syntaxe est convertie en prototype par le moteur JavaScript), comme dans les versions précédentes de JavaScript. Il s’agit surtout d’une couche syntaxique pour clarifier/simplifier le code.

const auteur = new Personne('viki53')
auteur.direBonjour() // "Bonjour, je m'appelle viki53"

Vous pouvez aussi utiliser l’héritage pour étendre une class parente (et une seule, pour le moment) :

class EtreVivant {
	constructor() {}

	get espece() {
		return this._espece
	}
}

class Dauphin extends EtreVivant {
	constructor() {
		super()
		this._espece = '🐬'
	}
}

const viki53 = new Dauphin()
viki53.espece // "🐬"

N’oubliez pas de vérifier le support des fonctionnalités que vous utilisez, par exemple via Can I use.
Par exemple pour les template literals, IE11 ne les gère pas. Edge 13, ainsi que Safari 9.1 et supérieurs, les gèrent (avec un bug connu sur Safari 12).

Maintenant que vous savez (presque) tout sur la syntaxe, on va pouvoir passer aux choses sérieuses ! :pirate: