Aller au contenu

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

async vs. defer

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 !
  • 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
    • !

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 simplement
  • Array.map(fn) qui appelle une fonction et renvoie une liste de même taille
  • Array.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))
  • inverser latitude/longitude (donc longitude en premier)
    • utilisez Array.reverse()
  • supprimer les points hors des bornes 48.84 > latitude > 48.86 et 2.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 ou false
  • utilisez les méthodes de tableau filter et map 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
  • 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"
      }
    }
  ]
};