JavaScript

- Présentation -

> Le JS késako ?

Un langage web

Le JavaScript a été créé en 1995 pour réaliser des petits scripts dans des pages Web afin de les dynamiser. Le langage est nativement supporté par tous les navigateurs Web, ce qui le rend extrêmement populaire. Cependant chaque navigateur à sa propre implémentation du langage, et donc son propre moteur JS (ce sera important pour la suite, enfin presque...)​

Un langage prototype​

Le langage respecte l'ensemble des paradigmes orienté objet, et est basé sur un principe de prototype. La principale différence entre un prototype et une classe étant qu'une classe est entièrement définie au build, un prototype est modifiable tout au long du runtime...​

Le JavaScript est un langage interprété. A l'inverse du C ou du Java, il n'est pas compilé, mais lu au fur et à mesure de son exécution pour le moteur JS (le plus souvent un navigateur Web).​

Un langage populaire​

L'utilisation première du JavaScript était de dynamiser des page HTML en créant des scripts qui s'exécutent côté client, donc directement sur le navigateur. L'intérêt est de diminuer les aller-retour client serveur pour les modifications mineurs de contenu Web.​

De là vont apparaitre de nombreuses librairies facilitant plus encore ces manipulation de contenu Web. La plus connue d'entre elle n'est autre que JQuery. Cette librairie Simplifie des fonctions complexe d'écoute d'évènement, de manipulation du DOM etc.​

Un langage populaire​

Attention, JS !== JQuery​

En effet c'est deux compétences sont régulièrement mélangées. JQuery n'est qu'une librairie qui s'appuie sur le langage JavaScript. Toutes les fonctions JQuery sont bien évidemment faisables en Vanilla JS*.

Ce module ne parlera que de JavaScript, pas de JQuery.

> Le JS après 2015​

Node.js et l'avènement des SPA​

En 2009 sort Node.Js. Il s'agit d'une plateforme logicielle libre qui, couplé a son gestionnaire de paquet NPM, permet de réaliser des application complexe en JavaScript, tel que des serveurs Web par exemple. L'arrivée de Node.js a permis notamment de créer des environnement de développement complexes et outillés (Babel, webpack, grunt, etc.).​

Node.js et l'avènement des SPA​

Depuis lors l'utilisation du JavaScript s'est démocratisée jusqu'à pouvoir créer des applications cross plateforme, compatible sur de nombreux devices :

  • Application desktop (Electron)
  • Application mobile (Cordova, Phonegap)
  • Application mobile (React native)
  • Serveur Web (Express)
  • Single Page Aplication (Backbone, Angular, React, Vue ...)

Les évolutions du langage​

Le JavaScript est normé par l'organisation ECMA-internationnal, et implémente notamment les normes ECMA-262 et ECMA-402. La version la plus connu dJavascript est certainement la version ECMAScript 5 qui est sorti en 2009. Mais depuis 2015 la norme évolue annuellement. On notera notamment une très grande différence entre ES5 et ES2015 (aussi appelé ES6).​

Les évolutions du langage​

ES5 (2009)​ Version la plus connue, supportée par tous les navigateurs​
ES 2015 (ES6)​ Ajoute énormément de modifications au langage​
... Evolution annuelle de la norme​
ES 2019​ Version actuelle de la norme​
ES next*​ Version destinée à devenir la nouvelle norme​

* ES.Next est un nom dynamique qui fait toujours référence à la prochaine version d'ECMAScript, en cours de rédaction. ​ Les fonctionnalités d'ES.Next sont plutôt considérées comme des propositions car la spécification n'a pas encore été finalisée​

Transpilage​

Le langage évoluant annuellement, il est difficile pour les acteur du marché d'être toujours à jours sur l'implémentation des moteurs JavaScript. Pour palier à ce manque, la communauté a créer des "traducteurs" capables de transformer un code écrit dans une version du langage en une autre version. Aussi des outils tel que Babel et autres vous permettent de transpiler votre JavaScript en une version compatible avec tous les navigateurs.

Transpilage​

Attention: Transpilage !== compilation

Le JavaScript reste un langage interprété. Aucune phase de compilation n'est présente. Le langage est simplement traduit, mais reste interprété au runtime par le navigateur.​​​​

Transpilage​

La phase de transpilage devenant de plus en plus fréquente, des surcouches au langage JavaScript ont fait leur apparition. Celles-ci ajoutent de nouvelle fonctionnalités, et des sucres syntaxiques. On pensera notamment au typage du langage avec TypeScript ou Flow. Encore une fois il ne s'agit que d'une surcouche, au final le code est la plupart du temps transpilé en JavaScript, et les types n'existent plus au moment de l’exécution du code. Il s'agit surtout d'un confort de développement.

NB: Nous n'aborderons pas les versions typées du langage.

> Les 6 piliers

Des types ou pas de types ?​

Bon OK on commence déjà par une contradiction. ​Le JavaScript est un langage non-typé.

Enfin presque... ​

En réalité il existe 6 types primitifs en JavaScript, les voici :

boolean null
Il s'agit du résultat d'une assertion logique. Les valeurs possibles sont true ou false​ type qui ne possède qu'une unique valeur null​
undefined number
représente la non-affectation d'une variable (on a donc une distinction entre une variablee affecté à null, et une variable non-affectée)​ ce type contient l'ensemble des valeurs numériques (entier, float, NaN, +Infinity, - Infinity)​
string symbol
représente une chaîne de caractères à partir d'un ensemble d'éléments 16bits non-signés​ type de données uniques et inchangeables (souvent utilisé pour créer des identifiants)​

Et donc​

Tout le reste est objet ! ​

Oui, oui, même les tableaux.

De plus un objet avec une call signature est une fonction.​

> Les objets

Présentation​

JavaScript est conçu autour d'un paradigme simple, basé sur les objets.​

Un objet est constitué d'un ensemble de propriétés. Une propriété est une association entre un nom (aussi appelé clé) et une valeur.​

La valeur d'une propriété peut être une fonction, auquel cas la propriété peut être appelée « méthode ».​

Création d'un objet​

							
								const obj = { first: "David", last: "Lopez", age: 77 };
							
						

Ici on crée donc une variable contenant un objet avec trois propriétés:​

  • first​
  • last
  • age​

Les valeurs de ces propriétés sont respectivement:

  • "David"
  • "Lopez"
  • 77​

Accéder à une propriété​

Pour accéder à la valeur d'une propriété d'un objet il existe deux syntaxes:​

							
								objet.propriete
								objet["propriete"]
							
						

Celles-ci sont la plupart du temps strictement identiques, exemple:​

							
								obj.first // => "David"
								obj["age"] // => 77
							
						

Affectation d'une valeur​

L'affectation d'une valeur se fait simplement grâce au symbole "=" après l'accès à la-dite propriété.​

							
								obj.first = "pierr";
								obj.first // => "pierr"
							
						

Affectation d'une valeur​

Les propriétés ne sont pas figée dans le temps. Il est possible de dynamiquement ajouter une propriété à un objet par simple affectation:

							
								const obj = { first: "david", last: "Lopez", age: 77 };
								obj.fonction = "Maitre du monde";
								obj.fonction // => "Maitre du monde"
								// Et parce que je fais ce que je veux depuis ES2016
								obj["a" + 12] = "Gino";
								obj.a12 // => "Gino"
							
						

Affectation d'une valeur​

Attention au piège si votre propriété est un nombre:​

							
								obj[77] = "roberto";
								obj[77] // => "roberto"
								obj["77"] // => "roberto"
								obj.77 // => SyntaxError
							
						

> Les strings

Présentation​

Le type String de JavaScript est utilisé pour représenter des données textuelles.​

C'est un ensemble d'"éléments" de valeurs non signées sur 16 bits (unités de codage UTF-16).​

Chaque élément occupe une position dans la chaîne de caractères. Le premier élément se trouve à l'indice 0, le suivant à l'indice 1 et ainsi de suite.​

Présentation​

La longueur d'une chaîne de caractères est le nombre d'éléments qu'elle contient.​

Une String est immuable : il est impossible de la modifier une fois créée. Il est cependant possible de recréer une string à partir d'une autre string.

Création d’une string​

Une string se crée en utilisant des simple quote (') ou des doubles quotes ("). Bien que la notation simple quote soit plus répandue, les deux notations fonctionnent parfaitement. On choisira l'une ou l'autre en fonction de la norme d'écriture de notre projet...​

							
								const maString = "Hello World!";
								const maDoubleString = "Hello World!";
								maString === maDoubleString // => true
							
						

Si vous ne connaissez pas le triple égal, il compare le type et la valeur.

Opérations​

L'accès à un élément d'une String se fait via son index :​

							
								maString[0] // => "H"
								maString[0] = "X"
								maString[0] // => "H"
							
						

Comme dit précédemment, une String est immuable. On ne peut changer les valeurs de ses éléments une fois crée.​

Comparaisons de Strings​

							
								"déjà" < "demain" 
								// => false; Cette comparaison ne fait pas du tout ce que vous pensez !
								"déjà".localCompare("demain") 
								// => -1; "Déjà" est alphatébiquement avant "demain"
							
						

​Attention aux pièges, les opérateurs "<" et ">" compare les valeur ASCII de la chaîne caractère par caractère. Donc pas d'ordre alphanumérique de cette manière.​ ​ ​

Autres opérations​

							
								"déjà !".toLocaleUpperCase() 
								// => "DÉJÀ !"; Renvoie une nouvelle instance en majuscule
								"ÇA POUTRE".toLocaleLowerCase()
								// => "ça poutre"; Renvoie une nouvelle instance en minuscule
								"one,two,three".split(",")
								// => ["one", "two", "three"]; Transformation en tableau selon un séparateur
								"one,,two,three".split(/\>W+/) 
								// => ["one", "two"]; Transformation en tableau selon une expression régulière
								"hello".substring(1)
								// => "ello"; Renvoie une nouvelle instance d'une partie de la chaine
								"hello".sline(1, -2)
								// => "el"; Renvoie une nouvelle instance d'une partie de la chaine
							
						

Interpolation de String​

Depuis l'ES2015, un nouvel opérateur est disponible pour gérer les String. Il s'agit de l'antiquote (`). Celui-ci permet, en plus de pouvoir insérer facilement des quote et double quote dans une String, de faire de l'interpolation de chaîne de caractère.​

							
								const person = { nom: "Dupont", prenom: "Michou" };​
								const stringAvant = 'Bonjour je suis ' + person.prenom + ' ' + person.nom;​
								const stringApres= `Bonjour je suis ${person.prenom} ${person.nom}.​
								// Et le javascript c'était pas mieux avant!`;​
							
						

> Les tableaux

Présentation​

Un Array est un ensemble ordonné de valeurs auxquelles on peut faire référence avec un nom et un indice.​

JavaScript ne possède pas de type particulier pour représenter un tableau. En revanche, il est possible d'utiliser l'objet natif Array ainsi que ses méthodes pour manipuler des tableaux.​

Création​

Les tableau sont créés via les symboles "[ ]"

							
								let names = ['John', 'David', 'Rodrigo'];​
								names.length // => 3​
							
						

Dès leur création un certain nombre de propriété sont initialisées, comme la propriété length représentant la taille du tableau.​​

Accès et affectation​

On accède à la valeur d'un élément d'un tableau via son index:​

							
								names[0] // => 'John'​
								// et si on affecte un élément loin dans le tableau ?​
								names[12] = 'Pierre';​
								names.length // => 13​
								names[9] // => undefined (comme de 3 à 11): c'est appelé "sparse array"​
								names; // => ['John', 'David', 'Rodrigo', undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 'Pierre']​
							
						

Attention: La propriété length représente la taille du tableau, et pas le nombre d'éléments définis dans le tableau.​

Concaténation​

Il est possible de concaténer deux tableaux via la méthode concat. Cette méthode ne modifie aucun des tableaux utilisés, mais crée une nouvelle instance de tableau et la renvoie en résultat.​

							
								let data = [1, 2, 3];​
								// arr1.concat(arg…) -> arr2 [déroule sur 1 niveau, ni "shallow" ni "deep"]​
								data.concat(4, 5, 6)     // => [1, 2, 3, 4, 5, 6]​
								data.concat([4, 5, 6])   // => [1, 2, 3, 4, 5, 6]​
								data.concat(4, [5, 6])   // => [1, 2, 3, 4, 5, 6]​
								data.concat([4, [5, 6]]) // => [1, 2, 3, 4, [5, 6]] -- 2 niveaux !​
								data                     // => [1, 2, 3] -- intact !​
							
						

Join​

La méthode join crée une String a partir d'un tableau en liant les élément via le séparateur renseigné. Si aucun séparateur n'est renseigné, une virgule est utilisée par défaut.​

							
								// arr.join([sep = ',']) -> String​
								data.join()   // => '1,2,3'​
								data.join('') // => '123' -- Fréquent en construisant du HTML​
							
						

Découpage​

La méthode slice découpe un tableau suivant les indexes de début et de fin renseignés. Attention aux valeurs par défaut et à l'utilisation d'indexes négatifs.​

							
								// arr1.slice(signedBegin[, signedEnd = length]) -> arr2 -- négatif ok partout !​
								data.slice(1)      // => [2, 3]​
								data.slice(1, 1)   // => []​
								data.slice(1, 2)   // => [2]​
								data.slice(1, -1)  // => [2]​
								data.slice(-2)     // => [2, 3]​
								data.slice(-2, 2)  // => [2]​
								data.slice(-2, -1) // => [2]​
							
						

Boucles

Depuis ECMAScript2015, le prototype ARRAY a gagné de nouvelles méthodes d'itération sur les éléments. Nous allons en présenter deux ici:​​

map immutabe Crée un nouveau tableau avec les résultats de l'appel d'une fonction fournie sur chaque élément du tableau appelant.​
forEach mutable Crée un nouveau tableau avec les résultats de l'appel d'une fonction fournie sur chaque élément du tableau appelant.​
							
								const ARRAY = [1,2,3,4];​
								for(let i = 0, _l=ARRAY.length; i < _l; i++ ){console.log(ARRAY[i], i)}​
								ARRAY.forEach(function(element, idx){console.log(element, idx)});​
								ARRAY.map(function(element, idx){console.log(element, idx)});​
							
						

> Les dates

Présentation

JavaScript ne possède pas de type primitif pour représenter des dates. Cependant l'objet Date et ses méthodes permettent de manipuler des dates et des heures au sein d'une application.​

L'objet Date possède de nombreuses méthodes pour définir, modifier, obtenir des dates. Il ne possède pas de propriétés.​

Présentation

JavaScript gère les dates de façon similaire à Java. (Donc assez mal...)​

Les dates sont représentées selon les nombres de millisecondes écoulées depuis le 1er janvier 1970 à 00h00:00. L'objet Date permet de représenter des dates allant de -100 000 000 jours jusqu'à +100 000 000 jours par rapport au 1er janvier 1970 UTC.​

Création​

On passe simplement par le constructeur de l'objet Date.​

							
								new Date()                 //  maintenant !​
								new Date(y,m,d[,h,m,s,ms]) // Valeur découpée. (un peu lourd...)​
							
						

Des méthodes pièges​

De manière générale les méthodes de l'objet Date sont peu utilisés, notamment à cause du biais cognitif que provoque le nom de certaine d'entre elle. On pensera notamment au getDate() qui renvoie le jour dans le mois, et pas du tout la date du jour.​

							
								const noel = new Date() (le 25 décembre 2018)​
								
								noel.getDate() // => 25 -_-'​
							
						

Des méthodes pièges​

							
								date.getYear()      // JAMAIS #Bug de l'an 2000 exemple: L'année 1976 renverra 76. Mais l'année 2016 renverra 116. -_-'​
								date.getFullYear()  // Mieux mais pas vraiment logique dans le nom...​
								date.getMonth()     // NAZE Java-like qui démarre à… zéro (janvier). #ugly​
								date.getDay()       // PERDUUUU ! C'est le DOW (Day Of Week), 0 = dim., 6 = sam​
								date.getDate()      // Le jour du mois.  Super logique.​
								date.getHours()​
								date.getMinutes()​
								date.getSeconds()​
								date.getMilliseconds() // "Milliseconds", un seul mot : une seule initiale.​
								
								// Heure locale du navigateur/système.  On a les mêmes en getUTCXxx()…​
							
						

Les Dates JavaScript sont généralement utilisées pour des comparaisons simples en millisecondes par exemple. Pour les cas plus complexe on préfèrera souvent utiliser des librairies du type Moment.js​

> Les opérateurs

Opérateurs arithmétiques​

+ Addition Somme des opérandes numériques ou bien la concaténation de chaînes de caractères​
										
											1 + 2 // 3 ​
											"toto" + "truc" // "tototruc"
										
									
- Soustraction Soustrait les deux opérandes pour obtenir leur différence.​
										
											3 - 5 // -2 ​
											"toto" - 3 // NaN
										
									
/ Division Quotient de ces opérandes avec l'opérande gauche comme numérateur et l'opérande droit comme dénominateur.​
										
											1 / 2 // renvoie 0.5 ​
											2.0 / 0 // renvoie Infinity
										
									

Opérateurs arithmétiques​

* Multiplication Produit des opérandes.​
										
											2 * 2 // 4 ​
											Infinity * 0 // NaN
										
									
% Reste Renvoie le reste de la division du premier opérande par le second. Le résultat obtenu a toujours le signe du numérateur​
										
											12 % 5 // 2 ​
											-1 % 2 // -1
										
									
** Exponantiation Renvoie le résultat de l'élévation d'un nombre (premier opérande) à une puissance donnée (deuxième opérande)​
										
											2 ** 3 // 8 ​
											3 ** 2 // 9
										
									

Opérateurs arithmétiques​

++ Incrément Ajoute une unité à son opérande et renvoie une valeur.​
  • Si l'opérateur est utilisé en suffixe (par exemple : x++), il renvoie la valeur avant l'incrémentation.​
  • Si l'opérateur est utilisé en préfixe (par exemple : ++x), il renvoie la valeur après l'incrémentation.
										
											var x = 3; ​
											y = x++; // y = 3, x = 4 ​
											
											var a = 2; ​
											b = ++a; // a = 3, b = 3
										
									
-- Décrément Soustrait une unité à son opérande et renvoie une valeur.​
  • Si l'opérateur est utilisé en suffixe (par exemple : x--), il renvoie la valeur avant la décrémentation.​
  • Si l'opérateur est utilisé en préfixe (par exemple : --x), il renvoie la valeur après la décrémentation.
										
											var x = 3; ​
											y = x--; // y = 3, x = 2 ​
											
											var a = 2; ​
											b = --a; // a = 1, b = 1
										
									

Opérateur d'affectation​

Affectation x = y x = y
Affectation après addition x += y x = x + y
Affectation après soustraction x -= y x = x - y
Affectation après multiplication x *= y x = x * y
Affectation après division x /= y x = x / y
Affectation du reste x %= y x = x % y
Affectation après exponentiation x **=y x = x ** y
Affectation après décalage à gauche x <<= y x = x << y
Affectation après décalage à droite x >>= y x = x >> y
Affectation après décalage à droite non-signé x >>>= y x = x >>> y
Affectation après ET binaire x &= y x = x & y
Affectation après OU exclusif binaire x ^= y x = x ^y
Affectation après OU binaire x |= y x = x | y

Les pièges​

Les opérateurs JavaScripts font des conversions implicites. Le JavaScript ne plantera jamais sur une opération. En revanche, il peut vous renvoyer une valeur native comme Nan, +Infinity etc.​ En voici quelques exemples:​

Les pièges​

							
								!42  => false​
								!!42  => true //pratique pour convertir n'importe quelle valeur en booleen​
								+'2'  => 2​
								"lala" + 42 => "lala42"​
								1/0 => Infinity​
								"lala" - 42 => NaN​
								
								{} + [] => 0 // Hum ok...​
								[] + {} => "[object Object]" // parce que why not!​
								{} + {} => "[object Object][object Object]" // Bon ça je veux bien​
								[] + [] => "" // Nan mais les gars...​
								[] - {} => NaN // -_-'​
								+[] => 0 // Ah ah​
								+[] + [] => "0" // Vous l'avez?​
								+[] + +[] => 0 // Bon ok j'arrete :=)​
							
						

Opérateurs de comparaison​

== true si les opérandes sont égaux après conversion en valeurs de mêmes types​
!= true si les opérandes sont différents après conversion en valeurs de mêmes types.​
=== true si les opérandes sont égaux et de même type.​
!== true si les opérandes ne sont pas égaux ou pas de même type.​
> true si l'opérande gauche est supérieur (strictement) à l'opérande droit.​
>= true si l'opérande gauche est supérieur ou égal à l'opérande droit.​
< true si l'opérande gauche est inférieur (strictement) à l'opérande droit.​
<= true si l'opérande gauche est inférieur ou égal à l'opérande droit.​

Opérateurs de comparaison​

De manière générale, et pour éviter les conversion implicite, on préfèrera utiliser l'opérateur "===" plutôt que "==".​

							
								42 == '42'        // => true  
								null == undefined // => true  
								null == 0         // => false 
								0 == undefined    // => false 
								0 == false        // => true  
								1 == true         // => true  
								42 == true        // => false 
								'0' == false      // => true  
								'' == false       // => true  
								NaN == NaN        // => false 
							
						

Opérateurs de comparaison​

De manière générale, et pour éviter les conversion implicite, on préfèrera utiliser l'opérateur "===" plutôt que "==".​

avec ===, fini de jouer : vérif valeur ET type !​

							
								42 === '42'        // => false​
								null === undefined // => false​
								null === 0         // => false​
								0 === undefined    // => false​
								0 === false        // => false​
								'0' === false      // => false​
								NaN === NaN        // => false -- rien à faire !
							
						

Opérateurs de logique​

Ces opérateurs servent à résoudre des expressions logiques en prenant en compte le caractère "Truthy" ou "Falsy" des valeurs de l'expression​

Attention: les opérateurs logiques &&, || et ! fonctionnent différemment des opérateurs binaire &, | et ~.​

Pour plus d'info: La doc MDN.​

Opérateurs de logique​

&& ET logique Vrai si les deux expressions ont des valeur Truthy. S'arrête à la première valeur Falsy​
|| OU logique Vrai si une des deux expressions a une valeur Truthy. S'arrête à la première valeur Truthy​
! NON logique Faux si l'expression a une valeur Truthy. Vrai sinon.​

Truthy et Falsy​

Le caractère Truthy ou Falsy d'une variable est déterminé comme suis, si la variable a l'une des valeur suivante :​

False / undefined / null / "" / 0 / NaN​

Alors elle est Falsy. Sinon elle est Truthy.​

Attention, Truthy et falsy sont différent de true et false!​

Truthy et Falsy​

							
								"true" == true => false 
								// la chaine de caratère "true" même après conversion n'est pas égale à true.​
								"true" && true => true 
								// la chaine de caratère "true" est Truthy pour ce qui est des expression logique!!​
							
						

> Destructuring / Spreading​

Présentation​

Depuis ES2015, des opérateurs de décomposition (Spread) et d'affectation par décomposition (Destructuring) ont fait leur apparition pour manipuler plus simplement les objets et les collections.​

La syntaxe de décomposition permet d'étendre un itérable en lieu et place de plusieurs arguments (pour les appels de fonctions) ou de plusieurs éléments (pour les tableaux) ou de paires clés-valeurs (pour les objets).​

Présentation​

L'affectation par décomposition est une expression JavaScript qui permet d'extraire des données d'un tableau ou d'un objet grâce à une syntaxe dont la forme ressemble à la structure du tableau ou de l'objet.​

Utilisation – Les tableaux​

							
								// Initialisation d'un array​
								const array = [1, 2, 3];​
								
								// On crée un nouvel array à partir de l'ancien​
								const array2 = [...array, 4, 5]; // array2 => [1,2,3,4,5]​
								
								function f(a, b, c) {​
									return a + b + c;​
								}​
								
								// On appelle la fonction à partir de l'array​
								f(...array); // => 1+2+3​
								
								// On récupère des éléments précis d'un array.​
								const [a, b, c, ...rest] = array2; // a=1; b=2, c=3, rest=[4,5]​
							
						

Utilisation – Les objets​

							
								// Initialisation d'un objet.​
								const objet = {a: 1, b: 2};​
								
								// On crée un nouvel objet à partir de l'ancien​
								const objet2 = {...objet, c: 3}; // objet2 = {a:1, b:2, c:3}​
								
								// On récupère des éléments précis d'un objet.​
								const {a, b, ...rest} = objet2; // a=1 b=2 rest={c:3}​
								
								// On peut aussi déstructurer directement dans une fonction :​
								function g({a, b, c: c2}) {​
									return a + b + c2;​
								}​
								
								g(objet2);​
							
						

JavaScript

- La programmation -

> Les fonctions

Présentation​

Les fonctions sont un peu la base de toute programmation... Le JavaScript n'échappe pas à la règle.​

En JavaScript, les fonctions sont des objets de première classe. Cela signifie qu'elles peuvent être manipulées et échangées, qu'elles peuvent avoir des propriétés et des méthodes, comme tous les autres objets JavaScript. Les fonctions sont des objets Function​

Afin de renvoyer une valeur, la fonction doit comporter une instruction return. Une fonction qui ne renvoie pas de valeur retourne undefined.​

Utilisation

Une fonction est créée avec le mot clé function ou par l'utilisation de la fat-arrow (celle-ci sera présentée en détail par la suite. Pour l'instant, il suffit de savoir qu'il s'agit d'une écriture simplifiée pour créer une fonction). Lors de la création il est possible de renseigner les paramètres utile au sein de la fonction.​ ​ ​

Utilisation

							
								// Fonction standard​
								function a() {​
									// this = contexte appelant​
								}​
								
								// Fonction standard avec paramètre​
								function ap(param1, param2) {​
									// this = contexte appelant​
								}​
								
								// Variable fonction anonyme (ne pas utiliser)​
								const b = function() {​
									// this = contexte appelant​
								}
							
						

Utilisation

							
								// Variable fonction nommée (usage assez rare)​
								const c = function c() {​
									// this = contexte appelant​
								}​
								
								// Lambda / "fat arrow" (anonyme)​
								const d = () => {​
									// this = contexte de la déclaration​
								}​
								
								// Lambda / "fat arrow" (anonyme) avec paramètres​
								const dp = (param1, param2) => {​
									// this = contexte de la déclaration​
								}​
								
								// fonction auto-appelante (usage particulier)​
								(function(){​
									// this = contexte appelant​
								})()​
							
						

Fonctions de classes / objets​

							
								// La syntaxe de a est un raccourci pour celle de b. (usage assez rare)​
								const e = {​
									a() {​
										// this = contexte appelant​
									},​
								
									b: function b() {​
										// this = contexte appelant​
									}​
								}​
								
								// Dans une classe​
								class G {​
									a() {​
										// this = contexte appelant​
									}​
								
									// Variable d'instance​
									b = () => {​
										// this = instance de la classe​
									}​
								}​
							
						

NB: Les notions de fat-arrow et de contexte sont détaillées plus tard.​

Appel d'une fonction​

							
								const addition = (a,b) => {​
									return a + b;​
								}​
								
								addition(1,2) // => 3​
								
								const objet = {​
									presentation(nom) {​
										console.log('Bonjour je suis ' + nom)​
									};​
								}
								​
								objet.presentation('Dupont Michou') 
								// => "Bonjour je suis Dupont Michou"
							
						

Appel d'une fonction​

A noter que si l'on ne renseigne pas les paramètres d'une fonction, celle-ci sera exécuté avec une valeur undefined pour ces paramètres. De la même manière il est possible de renseigner plus de paramètres que nécessaire. Ceux-ci seront simplement ignorés.​ ​ ​

							
								const addition = (a , b) => { return a + b };​
								
								addition(1) // => NaN (1 + undefined...)​
								
								addition(1,2,3) // => 3 (1+2)
							
						

> Les variables

Présentation​

Il existe trois façons de déclarer des variables en javascript:​

  • var: On déclare une variable dont la portée est celle de la fonction courante, éventuellement en initialisant sa valeur.​
  • let: On déclare une variable dont la portée est celle du bloc courant, éventuellement en initialisant sa valeur.​
  • const: On déclare une constante nommée, dont la portée est celle du bloc courant, accessible en lecture seule.​

var n'est plus vraiment utilisé dans les nouvelles normes. On préfèrera utiliser const dans la majorité des cas, et let dans les cas ou la variables est réaffectable.​

La portée d'une variable​

Les éléments créés en JavaScript ont une portée. Un Scope.​

Lorsqu'une variable est déclarée avec var en dehors des fonctions, elle est appelée variable globale car elle est disponible pour tout le code contenu dans le document. Lorsqu'une variable est déclarée dans une fonction, elle est appelée variable locale car elle n'est disponible qu'au sein de cette fonction.​

Avant la norme ECMAScript2015, il n'existait pas d'autre manière de déclarer des variables. Grâce aux mots clés const et let, il est possible de restreindre la portée d'une variable au bloc en cours (un bloc if par exemple).​

La portée d'une variable​

							
								// var, i.e. je fais ce que je veux de toute façon je suis crade​
								var a = 'lala';​
								a = 'toto'​
								a; // => toto ​
								
								if (true) { 
									var x = 5; 
								}​
								console.log(x); // x vaut 5, bref c'est pas top​
							
						

La portée d'une variable​

							
								// let et const c'est mieux!​
								const b = 5;​
								b = 6; // ERROR​
								
								let c = 5;​
								c = 6;​
								c; // => 6​
								
								if (true) {​
									let y = 5;​
									const z = 5​
								}​
								
								console.log(y); // ReferenceError: y is not defined, c'est mieux!​
								console.log(z); // ReferenceError: z is not defined, c'est mieux!​
							
						

>Les closures​

​Présentation​

Il est possible d'imbriquer une fonction au sein d'une fonction. La fonction imbriquée (interne) est privée par rapport à la fonction (externe) qui la contient. Cela forme ce qu'on appelle une Closure.​

Étant donné qu'une fonction imbriquée est une closure, cela signifie que la fonction imbriquée peut « hériter » du scope de la fonction parente. Et donc avoir accès aux variables de cette fonction.​

​Présentation​

De cette manière il est possbile de hierarchiser les portée des variables au sein d'un code JS.​

Les closures sont un bon moyen de rendre privée une partie du code. Elles seront très utiles pour créer des modules. Mais on y reviendra plus tard...​

​Utilisation​

							
								function externe(x) {​
									function interne(y) {​
										return x + y;​
									}​
								
									return interne;​
								}​
								
								var fn_interne = externe(3); // fn_interne est donc une fonction qui prend en parametre y en renvoie 3 + y​
								var resultat = fn_interne(5); // donc 8​
								
								var resultat1 = externe(3)(5); // renvoie 8​
							
						

​Utilisation​

Dans le cas présenté ci-dessus, on voit que la variable x est connu de la fonction nommé interne, celle-ci peut être utilisée normalement, mais ne sera initialisée qu'à l'appel de la fonction externe.​ Au moment de l'appel externe(3) , x est initialisé, et la variable résultat contient la fonction suivante:​

							
								function interne(y) {​
									return 3 + y;​
								}​
							
						

Du coup resultat(5) vaut 5+3 donc8!​​

​Utilisation

							
								function publicFx() {​
									let dateAppel = Date.now();​
								
									return function() { 
										console.log(dateAppel); 
									};​
								}​
								
								let privilegedFx1 = publicFx();​
								
								// Attendre un bref instant​
								let privilegedFx2 = publicFx();​
								
								// privilegedFx(1,2) sont en fait les fonctions internes construites au​
								// sein de publicFx, qui grâce aux règles de portée "voient"​
								// dateAppel.  Elles sont *closed over* par publicFx, ce qui fait​
								// que les valeurs de dateAppel au moment où les fonctions ont été​
								// renvoyéees sont préservées​
								privilegedFx1(); // => affiche la dateAppel du moment de la création de la fonction privilegedFx1!​
								privilegedFx2(); // => affiche la dateAppel d'après !​
							
						

Utilisation​

On peut voir ici exactement le même principe mais cette fois illustré avec des dates. On initialise deux fonctions privilegedFx1 et priviledgedFx2 qui contiennent chacune une fonction dans laquelle a été stockée une date correspondant à la date de création de la variable. Au moment de l'exécution les dates seront différente pour privilegedFx1 et privilegedFx2 car celles-ci on été initialisée à des instants différents.​

> Contexte et binding​

Présentation​

Le contexte d'une fonction est représenté par la variable this. Il est possible de faire appel à cette variable a tout moment dans le code, cependant la valeur de this sera déterminée à partir de la façon dont une fonction est appelée. Il n'est pas possible de lui affecter une valeur lors de l'exécution et sa valeur peut être différente à chaque fois que la fonction est appelée.​

Les différentes évolutions du langage ont permis de mieux définir cette variable indépendamment de la façon dont elle est appelée : bind, fat-arrow...​

​Binding implicite​

La valeur de this dépend de la façon dont la fonction qui l'utilise est appelée. De manière générale, lorsque this est utilisé dans une fonction, il aura la valeur de l'appelant de la fonction.​

​​Binding implicite

Le cas qui marche​

							
								const monObjet = {​
									firstName: 'Michou',​
									lastName: 'Dupont',​
								
									fullName() {​
										console.log(this.firstName + ' ' + this.lastName)​
									},​
								
									strictFullName() {​
										"use strict";​
								
										console.log(this.firstName + ' ' + this.lastName)​
									}​
								};​
								
								monObjet.fullName(); // => log Michou Dupont. Dans ce cas this === monObjet​
								monObjet.strictFullName(); // => IDEM​
							
						

​​Binding implicite

Ici on appel explicitement la méthode fullname() au travers de l'objet monObjet. Donc le contexte (this) au moment de l'exécution de la fonction vaudra l'appelant (i.e. ce qui est devant le ".", i.e. monObjet). La fonction qui s'exécute alors ressemblerait à ça:​​

							
								console.log(monObjet.firstName + ' ' + monObjet.lastName)​
							
						

Binding implicite​​

Le cas qui marche pas…​

							
								const newFullName = monObjet.fullname; const newStrictFullName = monObjet.strictFullname;​
								newFullName(); // => log undefined undefined. this vaut window.​
								newStrictFullName(); // => Error!!!!!!!! this vaut undefined​
							
						

Ici on définie une variable newFullName qui contient la méthode monObjet.fullName. Il est important de noter que newFullName ne contient pas le résultat, mais la fonction elle-même. newFullName est donc une fonction prête à être appelée.​

Binding implicite​​

Au moment de l'appel le javascript tente de lui initialisé son contexte, et regarde donc qui a appelé la fonction. Sauf qu'ici personne ne l'a appelée (i.e. il n'y a pas de point ni d'objet avant la fonction). Ici il existe deux cas:​

Le mode non-strict, le contexte vaut le contexte présent de la fonction. Donc ici this vaut window​

Le mode strict*, le contexte n'est simplement pas défini! this vaut undefined​

*Les différences entre mode-strict et mode "non-strict" sont très diverses. Il n'est pas prévu ici d'aborder le sujet en dehors des impactes sur le contexte. Cela pourra notamment expliquer de nombreuses erreurs telles que "impossible de trouvé firstname of undefined"​​

Binding implicite – Le problème​​

Dans une application on ne peut pas toujours certifier de l'appelant d'une fonction. Donc comment faire si je veux utiliser this mais que je ne maitrise pas l'appelant?​

							
								var name = 'Mr X';​
								
								let obj = {​
									name: 'Joe Lopez',​
								
									greet(whom) {​
										console.log(this);​
										console.log(this.name + ' salue ' + whom);​
									},​
								
									greetAll(first, second, last) {​
										console.log(this);​
										[first, second, last].forEach(this.greet);​
									}​
								};​
								obj.greet("les lopezs de France");​
								// => 'Joe Lopez salut les lopezs de France !'​
								
								let fx = obj.greet;​
								fx("l’atelier") // => '"Mr X salue l’atelier"'​
								obj.greetAll('David', 'Joe', 'Guénolé'); // => 'Mr X salue David, Mr X salue Joe, Mr X salue Guénolé'​
								// Le contexte est perdu au travers du foreach​
							
						

Binding implicite – Le problème​​

Au moment d'exécuter la fonction fx le contexte est perdu bon ça on vient de le voir. Mais plus délicat encore, au moment d'exécuter obj.greetAll, le contexte est lui aussi perdu, alors que là il y a bien quelque chose devant le "." et tout!​

Vous aurais-je menti?​

Et bien non, c'est le morceau du foreach qui pose problème, le contexte est perdu au moment où l'on renseigne le pointeur de fonction pour le foreach.​

Binding implicite – Les solutions​​​

Solution 1: La closure! (Bah oui, ce module est logique en fait...)​

Il suffit de définir une nouvelle variable contenant le contexte, et de l'utiliser dans une fonction imbriquée (une closure).​

Binding implicite – Les solutions​​​

							
								const obj = {​
									// …​
									greetAll(first, second, last) {​
										const that = this;​
										
										[first, second, last].forEach(function(name) {​
											that.greet(name);​
										});​
									}​
								}​
							
						

Ici lors de l'exécution on fera un that.greet et that aura été fixé au moment du premier appel, donc vaudra le contexte du moment de l'appel. Tadaaaaaaaaaaaa!​​

​Binding implicite – Les solutions​

Solution 2: Bind​

La fonction bind permet de déterminer le contexte d'une fonction qu'importe la façon dont elle est appelée. On surcharge le contexte quoi qu'il arrive avec l'objet qu'on aura mis en paramètre du bind.​

							
								const obj = {​
									// …​
									greetAll(first, second, last) {​
										[first, second, last].forEach(this.greet.bind(this));​
									}​
								}​
							
						

Ça marche vraiment bien, mais on a encore mieux!​

Binding implicite – Les solutions​​

Solution 3: la fat-arrow () => {}​

Cette notation permet de "binder" automatiquement le contexte englobant comme contexte de la fonction.​

							
								const obj = {​
									// …​
									greetAll(first, second, last) {​
										[first, second, last].forEach((name) => this.greet(name));​
									}​
								}​
							
						

Binding implicite – Les solutions​​

La fat-arrow c'est la vie...​

Petit exemple de factorisation de code avec la fat-arrow (avant c'était vraiment moche)​

							
								['add', 'remove'].forEach((action) => {​
									['change', 'status', 'error'].forEach((elt) => {​
										this[`${action}${capitalizeDefinition}${capitalize(elt)}Listener`] = (cb) => {​
											this[`${action}Listener`](`${def}:${elt}`, cb);​
										}​
									})​
								});​
							
						

> Call & apply

Présentation​

Les fonctions call et apply permettent de fixer le contexte au travers du premier argument lors de l'appel. La fonction call prendra par la suite une liste d'argument là ou apply utilisera un tableau.​

C'est une alternative au binding, mais qui se fait au moment de l'appel de la fonction, et non au moment de sa déclaration.​

							
								function ajout(c, d){​
									return this.a + this.b + c + d;​
								}​
								
								var o = { a:1, b:3 };​
								
								ajout.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16​
								ajout.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34​
							
						

JavaScript

- Les JS objet -

> Constructeur

Présentation​

Un constructeur est une fonction servant à initialiser un nouvel objet (jusque là tout va bien...). Le nom du constructeur est un peu comme « le nom de la classe ».​

Toute fonction peut servir de constructeur : il suffit de l’appeler avec l’opérateur new. Elle dispose alors d’une variable implicite this, qui représente la nouvelle « instance » (oui encore un contexte initialisé bizarrement)​

Une fois créé, le nouvel objet référence son constructeur via le mot clé constructor​

Utilisation

Je crée un constructeur de Person. (Par convention les fonctions constructeurs commence par une majuscule).​

							
								function Person(first, last) {​
									this.first = first;​
									this.last = last;​
								}​
							
						

Utilisation

Jusque là ça ressemble à n'importe quel constructeur, à la différence qu'ici les attribut de l'objet son définie dynamiquement dans la fonction. On a pas de classe structurée à proprement parler.​

							
								var joeLopezPerson = new Person('Joe', 'Lopez');​
								var davidLopezz = new Person('David', 'lopez');​
								
								joeLopezPerson.first // => 'Joe'​
								davidLopezz.first   // => 'David'​
							
						

La fonction constructeur retourne donc par défaut une instance de l'objet avec la valeur this.​

Utilisation

Du coup si on retourne un autre objet, il se passe quoi?​

							
								// Si on jouait aux cons ?!​
								function LopezPerson(first, last) {​
									this.first = first;​
									this.last = last;​
									
									return { first: 'Anne', last: 'Pas Lopez' };​
								}​
								
								var oops = new LopezPerson('Henry', 'Lopez');​
								oops.first // => Anne​
							
						

Bah oui, le constructeur est une fonction comme une autre, on peut donc retourner ce que l'on veut! C'est la valeur retournée par le constructeur qui servira a créer l'objet, peu importe comment vous avez rempli le this.​

> Prototype

Présentation

Les prototypes sont un mécanisme JavaScript qui permettent l'héritage de propriétés entre objets. Tout constructeur a un prototype, qui définit les propriétés (et donc par ce biais définit les méthodes) partagées par tous les objets issu d'un même constructeur.​

Un prototype est vivant, au sens où il est possible de le modifier au runtime. Si on le triture après l’appel au constructeur, ça marche quand même !​

Techniquement, y’a plein d’autres trucs dans un prototype (réf. au constructeur, gestion de propriétés…), le but n'est pas de détailler cela, mais d'expliquer le principe général du prototype.​

Utilisation​

On repart de notre constructeur de Person vu précédemment.​

							
								function Person(first, last) {​
									this.first = first;​
									this.last = last;​
								}​
							
						

Utilisation​

Puis on ajoute des méthodes au prototype du constructeur​

							
								// On augmente l'existant…​
								Person.prototype.fullName = function fullName() {​
									return this.first + ' ' + this.last;​
								}​
							
						

A partir de maintenant, tous les objets ayant été créé à partir du constructeur Person auront accès à une méthode fullName qui renvoie le nom complet de la personne.​

							
								var john = new Person('John', 'Smith');​
								john.fullName() // => 'John Smith'​
							
						

Utilisation

Il est aussi possible d'ajouter des méthodes a posteriori de la création de l'objet.​

							
								// Voici Kevin​
								var kevin = new Person('Kevin', 'DuTrenteQuatre');​
								
								// On ajoute une méthode​
								Person.prototype.greet = function greet() {​
									alert('Salut je m’appelle ' + this.first);​
								}​
								
								// Kevin sait se présenter. Bravo Kevin!​
								kevin.greet(); // => 'Salut je m’appelle Kevin'​
							
						

Les mauvaises pratiques :(​

Le JavaScript est très permissif. Aussi la syntaxe de prototype peut paraitre complexe au début. Il semblerait tout naturel de renseigner les fonction directement comme propriété de l'objet au moment de la construction.​

FAUX! c'est exactement l'exemple d'une chose à ne PAS faire! (oui je te vois venir avec tes idées bizarre! J'ai l’œil moi!)​

Les mauvaises pratiques :(​

							
								function Person(first, last) {​
									this.first = first;​
									this.last = last;​
									
									this.fullName = function fullName() {​
										return this.first + ' ' + this.last;​
									}​
								
									this.greet = function greet() {​
										alert('Salut je m’appelle ' + this.first);​
									}​
								}​
								
								var john = new Person('John', 'Smith');​
								john.fullName() // => 'John Smith'​
								john.greet()    // 'Salut je m’appelle John'​
							
						

Le problème ici est que les fonctions greet et fullName sont intégralement recopiées dans chaque instance de Person créée. C'est un gâchis pur et simple de mémoire...​

"Déguiser" un objet en tableau​

Un petit exemple pour aller plus loin, on fabrique un objet qui ressemble fortement à un tableau, puis on s'amuse avec les protoypes:​

							
								var fakeArray = { 0: '!', 1: 'ça torche', 2: 'JavaScript', length: 3 };​
								fakeArray.join = [].join; fakeArray.reverse = [].reverse;​
								
								fakeArray.reverse().join(' ');​
								// => 'JavaScript ça torche !'​
								
								// Ou alors :​
								fakeArray.__proto__ = Array.prototype;​
								fakeArray.reverse().join(' ');​
								// => 'JavaScript ça torche !'​
								
								// Méthodes « génériques » utilisables: 
								// concat, join, pop, push, reverse, shift,​
								// slice, sort, splice, toString, unshift.​
							
						

"Déguiser" un objet en tableau​

Ça peut paraitre inutile, mais c'est exactement ce principe qui a permis de créer les polyfill, très utiles pour coder sous IE9 avec le confort des nouvelles versions du langage :)​

> Classe

Présentation

Bon, depuis ES2015 on a tout de même un équivalent de classe. Ce n'est que du sucre syntaxique, mais c'est quand même vachement bien!​

							
								class TodoItem extends Component {​
									constructor(props, context) {​
										super(props, context);​
										
										this.state = {​
											editing: false​
										};​
									}​
								
								
									handleDoubleClick() {​
										this.setState({ editing: true });​
									}
								}​
							
						

> La chaîne d'appel

Présentation

Ici on va entrer un peu plus dans le détail du fonctionnement du JavaScript. On va s'intéresser à la façon dont le JavaScript résous un appel. Supposons que je fasse l'appel suivant :​

							
								obj.prop
							
						

Présentation

Quelle valeur va s'afficher, et comment est-elle résolue?​

  • On part de l'objet indexé obj​
  • Si la propriété prop se trouve parmi les propriété d'obj alors on renvoie la valeur contenu dans prop puis on s’arrête là​
  • Sinon on passe sur le prototype du niveau supérieur: celui du constructeur de l’objet en cours (on le trouve sous: constructor.prototype ou __proto__)​
  • De là on reprend à l'étape 2, sauf si on était déjà sur Object.prototype (dernier maillon de la chaine), auquel cas le lookup est fini, et échoue. On renvoie alors undefined.​

La preuve en live​

							
								function Person(first, last) {​
									this.first = first;​
									this.last = last;​
								}​
								
								Person.prototype.fullName = function fullName() {​
									return this.first + ' ' + this.last;​
								};​
								
								const davidLopez = new Person('David', 'Lopez');​
								
								davidLopez.first      // => 'David',         own property​
								davidLopez.fullName() // => 'David Lopez', Person.prototype​
								davidLopez.toString() // => '[object Object]', Object.prototype​
								
								Person.prototype.toString = function personToString() {​
									return '#Person ' + this.fullName();​
								};​
								
								davidLopez.toString() // => "#Person David Lopez"​
							
						

> Les modules

Présentation

Pendant de nombreuse années le JavaScript était un langage destiné à rendre dynamique des page HTML. Il était donc utilisé de manière ponctuelle à l'endroit désiré. L'évolution du web a fait que le JavaScript doit maintenant gérer des cas beaucoup plus complexe.​

Les modules JavaScripts permettent d'isoler votre code du reste de l'application, d'organiser votre code, d'éviter les collision de noms etc. Grâce à eux il est possible de faire des application complexe, multi-fichiers de manière organisée. Bon ok, ça révolutionne pas l'informatique, mais on se rend bien comte que c'est vite indispensable.​

Avant ES2015​

Imaginons une architecture fichier comme suit:​

							
								/app/​
									fichier1.js​
									fichier2.js​
							
						

Le fichier1.js publie une partie de son contenu via l'instruction module.exports. Dans l'exemple suivant seul l'objet monObjet est publié vers l'extérieur. La variable private est utilisable au sein du fichier JS pour tout calcul, mais n'est pas publié sur l'extérieur. On vient de rendre une partie du code privé.​

Avant ES2015​

							
								// fichier1.js​
								const private = 5;​
								
								const monObjet = {​
									name: "lala",​
									age: 42 + private,​
									awesomeness: true​
								}​
								
								module.exports = monObjet;​
							
						

Avant ES2015​

On accède au contenu d'un module via l'instruction require qui prend en argument le chemin du fichier qui exporte le module.​

							
								// fichier2.js​
								const lala = require('./fichier1.js');​
								lala.awesomeness; // true;​
								
								private; // undefined​
								lala.age; // 47​
							
						

Cette méthode est bien pratique pour exposer les partie du code que l'on veut, cependant elle reste assez limitée. On ne peut exposer qu'un seul objet par fichier par exemple.​

Après ES2015​

Avec l'évolution du langage la création et l'utilisation de module s'est simplifiée, tant dans l'écriture que dans les fonctionnalité proposées.​

On reprend la même structure:​

							
								/app/​
									fichier1.js​
									fichier2.js​
							
						

Après ES2015​

On utilise directement l'instruction export, qui nous permet notamment de définir un nom au moment de l'export. Il est possible d'exporter plusieurs objets.​

							
								// fichier1.js​
								export monObjet = {​
									name: "lala",​
									age: 42​
								}​
								
								export monObjet2 = {​
									name: "lola",​
									age: 8​
								}​
							
						

Après ES2015​

A l'import on peut choisir de n'importer qu'un fragment du fichier global (ça évite de tirer des dépendances inutile, et allège les fichiers js après transpilage). On peut aussi renommer à la volé le module importer etc.​

							
								// fichier2.js​
								import { monObjet, monObjet2 as renommage } from "./fichier1.js";​
								
								monObjet.age; // 42​
								renommage.age; // 8​
							
						

En réalité il existe différentes manières d'exporter et d'importer des choses via module. Le mieux reste d'aller voir la doc ;)​

Après ES2015​

							
								// fichier1.js​
								export default {​
									name: "lala",​
									age: 42​
								}​
								
								export const lolo = 28​,
							
						
							
								// fichier2.js​
								
								import lala from "./fichier1.js";​
								import * as monModule from './fichier1.js';​
								
								lala.age; // 42​
								monModule.lolo; // 28​
							
						

> JS asynchrone

Présentation​

Dans une application JS, vous aurez à gérer de l'asynchrone. Ne serait-ce que pour faire des appels à une API. Il existe plusieurs manière de gérer cela.​

Les callbacks​

Il s'agit de de la manière la plus simple de gérer l'asynchrone. Au moment de l'appel de la fonction asynchrone, on donne simplement une fonction appelée callback qui s'exécutera une fois la première fonction résolue. Voici un exemple avec la fonction setTimout. Il s'agit simplement d'une fonction qui ne fait rien, mais vous rend la main immédiatement et au bout du temps renseigné en deuxième paramètre exécute la callback renseignée en premier paramètre.​

Les callbacks​

							
								function delayedAlert() {​
									window.setTimeout(slowAlert, 2000);​
								}​
								
								function slowAlert() {​
									alert("That was really slow!");​
								}​
							
						

Attention le code de la callback est exécuté dans un contexte complètement différent! (je vous renvoie au module sur la programmation en JS pour les notions de contexte)​

Les promesses​

L'objet Promise est utilisé pour réaliser des traitements de façon asynchrone. Une promesse représente une valeur qui peut être disponible maintenant, dans le futur voire jamais.​

Une promesse a un état (pending, fullfilled, rejected). En fonction de sa résolution, elle appellera la callback associée via l'instruction then, qui renverra elle aussi une promesse.​

							
								promise.then(successCb,errorCb).then(otherSuccess, otherCb).catch(errorHandlingFn);​
							
						

Les promesses​

Voici le même exemple que précédemment géré avec des promesses:​

							
								var promise1 = new Promise(function(resolve, reject) {​
									setTimeout(function() {​
										resolve('foo');​
									}, 300);​
								});​
								
								promise1.then(function(value) {​
									console.log(value);​
									// expected output: "foo"​
								});​
							
						

Chaîne de promesses​

							
								fetch('/users.json')​
									.then(function(response) {​
										return response.json()​
									})
									.then(function(json) {​
										console.log('parsed json', json)​
									})
									.catch(function(ex) {​
										console.log('parsing failed', ex)​
									});​
							
						

Async / Await​

La déclaration async function définit une fonction asynchrone qui renvoie un objet AsyncFunction.​ Une fonction asynchrone est une fonction qui s'exécute de façon asynchrone grâce à la boucle d'évènement en utilisant une promesse (Promise) comme valeur de retour.​

L'opérateur await permet d'attendre la résolution d'une promesse. Il ne peut être utilisé qu'au sein d'une fonction asynchrone.​

Au final, c'est un sucre syntaxique autour d'une promesse!!​

Async / Await​

							
								function resolveAfter2Seconds(x) {​
									return new Promise(resolve => {​
										setTimeout(() => {​
											resolve(x);​
										}, 2000);​
									});​
								}​
								
								async function f1() {​
									var x = await resolveAfter2Seconds(10);​
								
									console.log(x); // 10​
								}​
								
								f1();​
							
						

JavaScript

- L'éco-système -

Présentation

Il existe probablement une librairie ou un utilitaire js pour l'intégralité des mots existants et inexistants. Il m'est même arrivé de taper une fois​

							
								npm install internet​
							
						

Bref, nous allons présenté ici quelques utilitaires JavaScript bien pratique ;)​

Lodash

Lodash est la librairie utilitaire la plus utilisée du monde JavaScript. Si vous voulez manipuler des objets, des collections etc. Lodash a certainement la fonction qu'il vous faut.​

  • Est ce que ma variable est un nombre / function / tableau => lodash/lang​
  • Filtrer, trier, réduire, etc. => lodash/{collection/object/array}​
  • Plein de fonctions prêtes à l'emploi => lodash/function​

C'est un peu la bibliothèque standard du JS ;)​

Moment

Les dates en JS, c'est pas vraiment la joie... Du coup on utilise moment. C'est bien plus pratique!​

  • Formater des dates moment().format('L')​
  • Manipuler des dates moment(maDate).add('days', 1)​
  • Gestion des timezone​

i18n

i18n vous sera utile pour gérer l'internationalisation de votre application. L'intégralité de vos ressources textuelles passeront par cette librairie pour pouvoir gérer le multilinguisme.​

Node.JS​

Node.js vous permet de créer des applicatifs écrits en javascript, et notamment des serveurs. On l'utilisera notamment pour éxécuter l'intégralité de la toolchain d'un projet SPA.​ ​ ​

Babel

Babel est le traducteur universelle du javascript. Il permet de transpiler le langage écrit dans une version évoluée d'ECMAScript en une version compréhensible pour n'importe quel navigateur.​ ​ ​

Webpack

Webpack est un ordonnanceur javascript utilisé pour builder les projets javascript. Il permet d'ajouter plusieurs modules à un pipeline de build.​

Au travers de ce pipeline de build, on passe d'un projet complexe avec des modules, des librairies etc. à un livrable composé de fichiers statiques à intégrer dans une page web.​

transpilage --> obfuscation --> compréssion --> postCss --> ...​

Npm​

Npm (Node Package Manager) est le gestionnaire de paquet qui vient avec Node.js. Il vous servira à gérer l'intégralité de vos dépendances javascripts.​ ​ ​