Langage avancé Généralités
async
/defer
#
Lors du chargement d’un JS externe via la balise <script>
, le parsing HTML est mis en attente pendant le chargement du fichier ET pendant l’exécution du code. Cela signifie qu’un script lent peut avoir un énorme impact sur le temps de chargement. Il existe deux attributs permettant de contrôler cela
defer
charge le fichier en parallèle (ne bloque pas le parsing HTML) mais attendra la fin du parsing HTML pour exécuter le code. L’ordre des scripts est préservé mais le contenu ne s’affiche qu’après.async
charge le fichier en parallèle (ne bloque pas le parsing HTML) et exécute le code directement (donc potentiellement bloque le parsing HTML), mais le contenu peut s’afficher tout de suite. L’ordre des scripts n’est par contre pas préservé.
L’évènement DOMContentLoaded
attends que l’ensemble des fichiers soient parsés et exécutés, sauf avec async
Mode strict#
Le mode strict est une variante restrictive de JavaScript, qui permet notamment d’éliminer certaines erreurs silencieuses (une exception sera levée), d’accélérer l’exécution du code (optimisation de certaines erreurs) et d’interdire l’utilisation de mot-clés réservés, même futurs.
Pour l’activer, placez 'use strict'
en haut des fichiers JS
Scope#
Exercice - O_o#
- Créer une liste HTML de 5 éléments
- Faire un boucle sur les éléments (en déclarant avec
var
)- Ajouter un évènement
click
sur chacun des éléments - Afficher en console le texte / la position de l’élément
- O_o !
- Ajouter un évènement
- Faire un boucle sur les éléments (en déclarant avec
let
)- Ajouter un évènement
click
sur chacun des éléments - Afficher en console le texte / la position de l’élément
- !
- Ajouter un évènement
En JS, les variables sont scopées au sein des fonctions uniquement.
var nb = 42
function foo () {
console.log(nb); // 42
}
function bar () {
var nb = 3.14;
function baz () {
console.log(nb); // 3.14
}
}
Cela pose notamment problème dans une boucle avec des fonctions anonymes
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
// affiche: 5, 5, 5, 5, 5
La variable i
est définie de manière globale, et la fonction anonyme n’est stockée qu’une seule fois. La fonction utilise alors la dernière valeur connue (après la boucle). Pour gérer ce cas, il faut alors utiliser une fonction nommée et ainsi scoper la valeur de la variable
function foo (i) {
setTimeout(function () {
console.log(i);
}, 0);
}
for (var i = 0; i < 5; i++) {
foo(i);
}
// affiche: 0, 1, 2, 3, 4
// ou avec une IIFE (Immediately Invoked Function Expression)
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i);
}, 0);
})(i);
}
Avec ES6 et le mot-clé let
, les variables peuvent être scopées par block, et cela simplifie le code
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
// affiche: 0, 1, 2, 3, 4
Pour le cas des évènements, la fonction ne peut pas être appelée directement dans le callback (le paramètre listener
doit être une référence à la fonction, et pas un appel à cette fonction.)
function onClick () {
console.log('Click');
}
// OK
monElement.addEventListener('click', onClick);
// Pas OK: la fonction sera appelée avant même le click
monElement.addEventListener('click', onClick());
Pour passer des paramètres à cette fonction, il n’est donc pas possible de faire
monElement.addEventListener('click', onClick(param));
Dans ce cas, deux solutions :
// fonction anonyme qui appelle notre fonction
monElement.addEventListener('click', function() { onClick(param) });
// utilisation de bind (changement de contexte)
monElement.addEventListener('click', onClick.bind(null, param));
Attention alors au problème de scope.
Programmation fonctionnelle#
La programmation fonctionnelle est un paradigme particulier qui repense le concept de boucles, en le remplaçant par l’appel à des fonctions pour chaque élément d’une liste. Il existe plusieurs méthodes, mais notamment :
Array.forEach(fn)
pour itérer simplementArray.map(fn)
qui appelle une fonction et renvoie une liste de même tailleArray.filter(fn)
qui appelle une fonction et renvoie une liste de même taille ou plus petite (filtrée)
Exercice - Traitement géo (programmation fonctionnelle)#
Voici une liste de coordonnées (île de la Cité et île St-Louis) :
let polygons = [[[[48.857757564566924,2.339316137485028],[48.856806083163775,2.3410084512700275],[48.85500429223433,2.3431007664951173],[48.85424556800601,2.3448607983431384],[48.85346104822773,2.3470146522513193],[48.852722069967,2.348637735017842],[48.852246283670866,2.3498685086796596],[5.513516521235,3.512416589454425],[48.85158827389591,2.352683903431068],[48.85202863524094,2.352391594686386],[48.85372424333016,2.3520685166001587],[48.8546352926156,2.3512762060553634],[48.85546534530812,2.3495454305934325],[48.855769019683805,2.3483223492670007],[48.85667497062573,2.3452915691247744],[48.857282302859346,2.3412992470592533],[48.857757564566924,2.339316137485028]]],[[[48.85374747782521,2.3530207906753438],[48.85253332128681,2.3532408660072544],[48.85163103910017,2.353884163131301],[48.85105178764471,2.3558479122468126],[48.849893264629145,2.3592336865839],[48.84948109132219,2.360317134371768],[48.85032771390132,2.36001241468143],[48.85104064812796,2.3601478456549136],[48.85167559662685,2.35960612176098],[48.85373633890825,2.353410154724109],[48.85374747782521,2.3530207906753438]]]]
Nous voulons réaliser un traitement qui doit :
- arrondir les coordonnées à 5 chiffres après la virgule
- utilisez
Number(nb.toFixed(val))
- utilisez
- inverser latitude/longitude (donc longitude en premier)
- utilisez
Array.reverse()
- utilisez
- supprimer les points hors des bornes
48.84 > latitude > 48.86
et2.33 > longitude > 2.37
Pour cela :
- créez trois fonctions génériques :
- une pour arrondir un seul nombre (prends un nombre en entrée, retourne le nombre arrondi)
- une pour inverser un tableau (prends un tableau en entrée, retourne le tableau inversé)
- une qui vérifie si le point x/y passé en paramètres est entre les bornes souhaitées. La fonction doit retourner
true
oufalse
- utilisez les méthodes de tableau
filter
etmap
pour appeler ces fonctions et effectuer le traitement- les deux premières fonctions avec
map
- la dernière fonction avec
filter
- les coordonnées sont un tableau, qui contient 2 polygones, chaque polygone contient plusieurs formes, chaque forme a un jeu de coordonnées
- les deux premières fonctions avec
- le tableau final doit avoir la même structure que celui de départ
Vous pouvez tester le résultat sur geojson.io :
- ajoutez votre variable finale dans l’objet geoJSON ci-dessous
- récupérez le geoJSON en mode texte avec
JSON.stringify(val)
let geojson = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [],
"type": "MultiPolygon"
}
}
]
};