Expertise (5/5)
J'utilise régulièrement JavaScript depuis 2 ans. J'ai développé de nombreux greffons pour JQuery.
- Intégration de Google Map (sélection d'un lieu avec la souris).
- Carrousel de photos.
- Trombinoscope animé.
- Slider (dans toutes les directions).
- ... et de très nombreuses autres fonctions plus ou moins spécifiques.
Ce document contient quelques exemples simples de réalisation.
- Déclaration d'une variable:
var x;
- Assignation d'une valeur à une variable:
x = 10;
La portée des variables déclarées dans une fonction est limitée à la fonction. Mais, contrairement à d’autres langages (C, C++, Java, GO...),
la portée des variables déclarées dans un bloc ({...}
) n’est pas limitée au bloc. De nombreux développeurs sont spécialisés dans des langages
tels que C, C++ ou Java et ce dernier point peut engendrer de nombreuses erreurs: en déclarant des variables dans des blocs,
ils pensent que la portée de ces dernières est limitée aux blocs dans lesquels sont déclarées. C'est faux.
Le script ci-dessous utilise une fonction anonyme pour « isoler » les variables utilisées dans le bloc d’une boucle.
La variable x n'est pas définie en dehors de la boucle. En revanche la variable i est définie. Pour limiter la portée de la variable i
on peut utiliser une fonction anonyme de la façon suivante:
Note importante sur le processus de "déclaration implicite":
la déclaration d'une variable peut être effectuée de façon implicite lors de la première assignation d'une valeur à une variable
(sans utilisation du mot clé var).
Par exemple
var i = 0; // Déclaration explicité de la variable i.
var f = function() {
x = 10; // Déclaration **implicite** de la variable x.
};
Ce mécanisme de déclaration implicite est la cause de très nombreux dysfonctionnements extrêmement difficiles à diagnostiquer. En effet, il est très
facile d’oublier d’utiliser le mot clé
var. Et, dans ce cas, l’interpréteur JavaScript ne lance aucun avertissement.
L’interpréteur va « remonter » les « zones de portées des variables » à la recherche de la variable.
- Si une variable de même nom est déclarée dans une « zone de portée » alors la nouvelle valeur sera assignée à la variable.
- Sinon (si l’interpréteur « remonte » jusqu’à la zone de portée « terminale »), l’interpréteur crée une variable globale.
Illustration:
Traitement de la variable x:
- La variable x n'est pas déclarée dans la function anonyme "of2()". Donc l'interpréteur va « remonter »: il cherche la déclaration de la variable x dans la fonction anonyme "of1()".
- La variable x n'est pas déclarée dans la function anonyme "of1()". Donc l'interpréteur ba « remonter »: il cherche la déclaration de la variable x dans la fonction "f2()".
- La variable x n'est pas déclarée dans la function anonyme "f2()". Donc l'interpréteur va « remonter »: il cherche la déclaration de la variable x dans la fonction "f1()".
- La variable x est déclarée dans le function "f1()". Donc l'interpréteur assigne la valeur 3 à la variable x déclarée dans la fonction "f1()".
Traitement de la variable y:
- La variable y n'est pas déclarée dans la function anonyme "of2()". Donc l'interpréteur va « remonter »: il cherche la déclaration de la variable y dans la fonction anonyme "of1()".
- La variable y n'est pas déclarée dans la function anonyme "of1()". Donc l'interpréteur va « remonter »: il cherche la déclaration de la variable y dans la fonction "f2()".
- La variable y n'est pas déclarée dans la function anonyme "f2()". Donc l'interpréteur va « remonter »: il cherche la déclaration de la variable y dans la fonction "f1()".
- La variable y n'est pas déclarée dans la function anonyme "f1()". Donc l'interpréteur va « remonter »: il cherche la déclaration de la variable y dans la zone de portée terminale.
- La variable y n'est pas déclarée dans la zone de portée terminale. Donc l'interpréteur va déclarer la variable y dans la zone de portée terminale. y est donc une variable globale.
Résultat à l'exécution:
Le "hoisting" (to "hoist" peut être traduit pas "hisser" ou "lever") est une caractéristique originale du JavaScript. Ce mécanisme, s'il n'est pas bien
compris par le développeur, peut engendrer des erreurs extrêmement difficiles à détecter.
Le "hosting" consiste à déplacer les déclarations des variables au début des blocs qui délimitent leurs portées.
Autrement dit:
Est équivalent à:
Mais, attention, ce n'est pas équivalent à:
Note importante sur le mécanisme de "hoisting": le mécanisme de "hoisting" produit parfois des effets difficiles à prédire.
Résultat à l'exécution:
Vous constatez le message suivant:
ERROR: y is not defined (you should understand: y is not **declared**)
Wikipedia: http://fr.wikipedia.org/wiki/Fermeture_%28informatique%29
Dans un langage de programmation, une fermeture ou clôture (en anglais : closure) est une fonction qui capture des références à des variables libres
dans l'environnement lexical. Une fermeture est donc créée, entre autres, lorsqu'une fonction est définie dans le corps d'une autre fonction et fait
référence à des arguments ou des variables locales à la fonction dans laquelle elle est définie.
La nature asynchrone du JavaScript rend indispensable l’utilisation des closures. En effet, entre le moment où un gestionnaire d’évènement est exécuté,
et le moment où il a été préalablement défini, les valeurs des variables utilisées par le gestionnaire peuvent avoir été modifiées. Il est donc indispensable
de « capturer » la valeur d’une variable à un instant T afin de pouvoir utiliser cette valeur ultérieurement. Pour cela on utilise la construction suivante :
var eventHandler = function(valueToCapture1, valueToCapture2) {
return function() {
// Use the captured values.
console.log("(valueToCapture1, valueToCapture2) = " + valueToCapture1, valueToCapture2);
};
}
Exemple d'utilisation
ou:
Dans le code "handlers.push(eventHandler(i*2, object))
", "i*2
" est transmis pae copie. En revanche "object
" est
transmis par référence. Mais à chaque appel à la fonction "setTimeout()
", une nouvelle référence est transmise. Ainsi les fonctions retournées
par l'appel à "eventHandler()
" utilisent des instances distinctes d'objets.
Le code "var object = { a: i }
" signifie que la valeur de "object
" est une référence vers un objet.
A chaque passage dans la boucle, "object
" prend une nouvelle valeur (car un nouvel objet - "{ a: i }
" - est créé).
Ainsi, à chaque appel de la fonction "
eventHandler()
", le paramètre "
valueToCapture2
" (qui est une référence vers l'objet "
object
") prend une nouvelle valeur.
Résultat à l'exécution:
Remarque sur l'utilisation d'objets (passage par référence)
Maintenant, cosidérons le code ci-dessous:
Résultat à l'exécution:
Contrairement à l'exemple précédent, on constate que la valeur affichée de "valueToCapture2.a
" ne change pas (elle vaut 2).
Cette valeur correspond à la valeur de "i
" lors du dernier passage dans la boucle.
Explication: contrairement à ce qui se passe dans l'exemple précédent, la valeur de "valueToCapture2
" ne change pas d'un passage
à un autre dans la boucle. Cette valeur est égale à la référence de l'objet "object
".
Pour obtenir le comportement "attendu", une solution serait de créer un clone de l'objet
object
, comme dans l'exemple ci-dessous.
Cette section présente deux techniques pour la création d’objets :
- La première technique consiste à définir un prototype qui servira à la construction de l’objet. Ce prototype définit les propriétés et les
méthodes de l’objet. Les éventuelles opérations d’initialisation de l’objet sont effectuées dans le constructeur.
- La seconde technique consiste à se passer de prototype : la définition des propriétés et des méthodes de l’objet est incluse dans le
constructeur. Ce dernier effectue également les éventuelles opérations d’initialisation de l’objet.
Technique 1: avec prototype
Technique 2: sans prototype
Ces deux techniques ne sont pas équivalentes. Elles diffèrent, principalement, sur deux points :
- La deuxième technique consomme plus de ressources. En effet, à chaque instanciation d’un nouvel objet, les propriétés et les méthodes
sont clonées. Ce n’est pas le cas si l’on définit un prototype.
- La modification d’un prototype entraine la modification instantanée de tous les objets instanciés via ce prototype.
Ainsi, si l’on désire ajouter une propriété à toutes les personnes, il suffit d’ajouter cette propriété au prototype ayant servi à l’instanciation des personnes.
Illustration:
A l'exécution:
Le code ci-dessous présente deux techniques qui permettent de faire hériter un objet des propriétés et des méthodes d’un autre objet.
A l'exécution:
Et:
Ces deux techniques ne sont pas équivalentes. Veuillez consulter la rubrique Constructeurs et prototypes.
Cet exemple illustre une utilisation du greffon. Une série de 6 images défile sur trois "cases".
- La fréquence de remplacement d'une image par une nouvelle image a été paramétrée à 1 image par seconde. Ce paramètre de configuration est configurable.
- Les images sont cliquables. Chaque image est associée à une URL. Cette association est facultative. Si une image n'est pas associée à un lien, alors elle n'est pas cliquable.
Code du greffon
Utilisation
Création de la zone d'affichage des images
Une image est affichée dans un "bloc DIV". Ce bloc est identifié par son nom, associé à l'attribut "id". Le code ci-dessous crée trois blocs alignés horizontalement. Chaque bloc est utilisé pour afficher une image.
Création du trombinoscope
- On associe un trombinoscope à une liste de zones d'affichage (en l'occurrence "canvas1", "canvas2", "canvas3").
- On définit la fréquence de remplacement d'une image (ici, on remplace une image par une autre toutes les 1 secondes.
- On spécifie que l'on ne désire pas activer le "mode aléatoire". Dans le mode aléatoire, l'image à remplacer est choisie aléatoirement.
Chargement du Javascript et démarrage du trombinoscope
L'utilisation du greffon de trombinoscope nécessite le chargement de l'excellent greffon suivant :
https://github.com/farinspace/jquery.imgpreload
Ce greffon très simple permet d'appliquer des liens sur des zones du document.
- Un lien sur un texte.
- Un lien sur un (autre) texte.
Un lien sur une image:
Code du greffon
Utilisation
Pour associer un lien à un élément du document, il suffit d'ajouter à l'élément un attribut dont la valeur pointe vers le lien désiré. Dans le code ci-dessous, nous avons choisi d'utiliser l'attribut "link". Le choix du nom de l'attribut est arbitraire. Ce nom sera utilisé lors de l'activation du code Javascript (le nom de l'attribut sera passé en argument au greffon).
Création de la zone d'affichage des liens
Chargement du Javascript et démarrage
Il est courant d'associer plusieurs noms de domaines à un même site. Or, les clés d'utilisation de l'API Google sont liées à un seul nom de domaine. Le code ci-dessous permet une initialisation de l'API Google quel que soit le nom de domaine utilisé.
Si vous développer un site communautaire, il peut être intéressant de déporter les calculs des âges de vos membres sur le client.
Code du greffon
Démonstration
Note: Les liens "précédent" et "suivant" peuvent être remplacés par des images, des boutons... Ces liens peuvent être disposés n'importe où sur le document.
Utilisation
Création des zones de défilement
Les diapositives sont définies dans une liste non indexée.
Les contrôles de défilement sont identifiés par leur ID, construit à partir de l'ID de la zone de défilement des diapositives. Si "IZ" est l'ID de la zone de défilement des diapositivesn alors :
- ID du contrôle pour afficher les diapositives suivantes : "IZNext"
- ID du contrôle pour afficher les diapositives précédentes : "IZPrev"
La direction de défilement est définie par la valeur de l'attribut "direction".
- direction="horizontal": Défilement horizontal
- direction="vertical": Défilement vertival
Première zone : défilement horizontal
Seconde zone : défilement vertical
Chargement du Javascript et démarrage
Note: Le sélecteur JQuery ("[slider]", en l'occurrence) est déterminé en fonction des noms des attributs associés aux zones de défilement des diapositives. Dans cet exemple, les deux zones de défilement se voient attribuer le même attribut ("slider", en l'occurrence). Ceci n'est pas une obligation. Il est possible d'attribuer des attributs différents. Dans ce cas, la méthode devra être appelée autant de fois qu'il y a d'attributs différents.
Le code ci-dessous illustre l’utilisation de Jquery pour le chargement de données et de code à exécuter.
js_to_load_and_execute.js
json_to_load_and_execute.js