Bases de programmation illustrées en javascript
Olivier Pons
(olivier.pons@lecnam.net)
2022
On fera une utilisation intensive du
mode calculette
Pour tester de façon interactive les premiers exemples.
F12
puis onglet console)Fournissent une boucle d'interaction (qu'on appel
aussi Toplevel
ou REPL
(Read Eval Print
Loop)).
Tout les premiers exemples sont à tester à ce niveau.
Dans les exemples ce que vous tapez est précédé du caractère d'invite
\(>\).
Le réponse de l'interpréteur suit.
Tous le code javascript que nous écrirons comprendra des commentaires.
Dans le but d'expliquer ou de documenter le code.
Des bouts de code ignorés lors de l'évaluation du code par l’interpréteur javascript
//
/*
(debut) et
*/
(fin)Elle peuvent être de différent types.
number
)Pas de différence (de type) entre flottant et entier. C'est
Number
pour les deux.
string
)Délimitées par des guillemets simples '...'
ou doubles
"...."
.
Il n'y a pas de type spécifique aux
caractères.
Un caractère est juste une chaîne composée de cet unique caractère.
On peut aussi délimiter les chaînes par guillemets inversés (back quote) ` . On verra plus loins que cela permet d'interpreter du code dans une chaîne.
boolean
)2 valeurs: true
et false
On reparlera de la première au moment des variables et de la seconde au moment des "objets"
Ou l'on voit la boucle d'interaction javascript (Read, Eval, Print, Loop) comme une calculatrice.
Les opérations disponibles dépendent des types:
+
, *
/
-
(unaire et binaire) %
modulo (reste de la
division entière)> 2+3
5
> 2.45*45
1 10.25000000000001
> 99.9/3
3 3.300000000000004
> 21/2
10.5
> 4/8
0 .5
> 100000000000000
1 00000000000000
> 1000000000000000000000 //notation scientifique
1 e+21
> 2e+20
2 00000000000000000000
> 9%2 //modulo
1
> a= -1/0; //on a des elements représentants l'Infini
-Infinity
>
Math
contient plein de fonctions (methodes) mathématiques> Math.sqrt(36)
6
> Math.round(99.9)
100
> Math.floor(99.9)
99
> Math.sin(Math.PI)
1.2246467991473532e-16
> Math.random() // nombre aléatoire en 0 et 1
0.6259868469814811
> Math.random()
0.8644534713097394
> Math.sqrt(-1) // la racine d'un nombre negative n'existe
NaN // on renvoi un : Not a Number
Pour la division euclidienne (entière) de x par y, on pourra faire :
q= Math.floor(x/y);
(Math.floor() calcule la partie entière).
> "bonjour"[0]
'b'
> "bonjour"[5]
'u'
> "bonjour".charAt(5) // equivalent du precedent
'u'
> "bonjour"["bonjour".length-1] //dernier élément
'r'
> "bonjour"["bonjour".length] //on va trop loin...pas de valeur
undefined
>
> "bonjour".indexOf("j")
3
> "bonjour".indexOf("ou")
4
> "bonjour".indexOf("oui") //si pas present renvoi -1
-1
>
Les chaines sont encodés en UTF-16. Sur les characters non accentué cela correspond au code Ascii.
Quand vous aurez acquis un certain niveaux de javascript vous pourrez aller lire ca.
On peut acceder au code d'un caractère par
charCodeAt(position_dans_la_chaine)
et retrouver le
caractère (chaîne de longueur 1) avec
String.fromCharCode(code)
.
> "A".charCodeAt(0)
65
> String.fromCharCode(66)
'B'
> String.fromCharCode(67) // les caractères sont dans l'ordre alphabétique
'C'
> "a".charCodeAt(0) // mais les minuscules sont après les majuscules
97
> "b".charCodeAt(0)
98
"machaine".
puis utiliser la complétion de
l'éditeur (ou de la console du navigateur)&&
(et) et ||
(ou) et
!
(non)&&
et ||
sont
paresseux c'est à dire:
si le premier argument de &&
est faux il ne
calcule pas le second
si le premier argument de ||
est vrai il ne calcule pas le
second
===
,
>
<
>=
<=
!==
On rencontrera aussi, ==
et !=
mais ils
sont est plein de pièges et donc à éviter en général (nous verrons
pourquoi)
3<=3
true
> 1E10>Math.pow(2,100)
false
> "3"===3
false
> "3"==3 // A EVITER conversion implicite source d'erreur !
true
> true<false
false
> false<true
true
! true===false
> ! true===false
true
>
Les comparaisons sur les chaînes utilisent l'ordre
alphabétique dans l'encodage des caractères (UTF-16).
Les majuscules sont situées avants les minuscules.
> "ABC"<"DEF"
true
> "abc"<"def"
true
> "ABC"<"def"
true
> "abc"<"DEF"
false
>
> "111">"12" //sur des chaines le caractère '1' est avant le caractère '2' dans "*l'alphabet*"
false
> 111>12 //sur des nombres
true
typeof
> typeof "une chaine"
'string'
> typeof "bonjour"[1]
'string'
> > typeof ("1"+2)
'string'
>
> typeof NaN //Not a Number est de type number !
'number'
> typeof Undefined
'undefined'
> typeof null
'object' // on parlera du type object plus loin ;-)
>
NaN
est un number
qui correspond à une
operation impossible sur les entiers.
il se propage c'est à dire que si une partie d'un calcul est impossible
alors tout le calcul est impossible
NaN
est le seul nombre... pas égal à lui même. Pour le
tester, il faut utiliser isNaN()
:
C'est source d'erreur pénible à detecter...
=
ne sert pas a comparer, c'est un opérateur
d’affectation
Opérateur | : | Description |
---|---|---|
a == b |
: | Égal, après conversion de type |
a != b |
: | Différent, après conversion de type |
a === b |
: | Égal et de même type |
a !== b |
: | Différent ou de type différent |
a < b |
: | Strictement plus petit après conversion de type |
a > b |
: | Strictement plus grand, après conversion de type |
a <= b |
: | Plus petit, après conversion de type |
a >= b |
: | Plus grand, après conversion de type |
On a vu la notion de valeur (et les calculs qu'on peut faire avec ces valeurs).
On peut stoker ces valeurs dans des variables (ou des constantes).
On peut conceptualiser une variable comme une boite, dans laquelle on peut stoker l'information.
On peut éventuellement modifier le contenue de la boite.
Concrètement c'est un nom qui renvoie à une position en mémoire dont le contenu peut potentiellement prendre successivement différentes valeurs pendant l'exécution d'un programme.
Un nom de variable (ou de constante) commence toujours par une
lettre (majuscule ou minuscule), $
ou _
et se
poursuit par un de ces caractères ou un chiffre.
Les variables sont déclarées au moyen du mot clé
let
(portée bloc) et historiquement par
var
(portée fonctionnelle)
On reparlera de la notion de portée plus loin apres avoir vu les
fonctions.
la déclaration d'une variable crée la variable. dans le
mode interactif cela affiche undefined
.
si elle n'est pas accompagné d'une initialisation alors la variable vaut
undefined
.
Les variables sont initialisées, ou
mises à jour avec l'opérateur
d'affectation =
l'operation d'affectation renvoie la valeur affectée sauf si elle est
faite en même temps que la declaration ou elle renvoie
undefined
Les constantes sont déclarées au moyen du mot
clé const
Les constantes une fois initialisée avec l'opérateur
d'affectation =
ne peuvent plus être mises
à jours Le valeur ne chargera plus dans le reste du programme.
Les constante et les variables déclarées par let
ne
peuvent être redéfinies (par var
si mais c'est aussi source
d'erreur, surtout si on bosse à plusieurs sur un gros developments
!)
Exemples :
> let pi = 3.1415926;
undefined
> let _2pi = 2*pi; // le nom ne peut commencer par un chiffre !
undefined
> let 2pi= 2*pi; // le nom ne peut commencer par un chiffre !
let 2pi= 2*pi; // donc erreur de syntaxe !
^
Uncaught SyntaxError: Invalid or unexpected token
> pi = 3.14; //mise à jour de la variable
3.14
> const vitesseDeLaLumiere= 299792458
undefined
> vitesseDeLaLumiere= 300000 // on ne peut modifier une constante
Uncaught TypeError: Assignment to constant variable.
> let ecrivain="Romain Gary"
undefined
> let ecrivain="Emile Ajar" // on ne peut la redéfinir
Uncaught SyntaxError: Identifier 'ecrivain' has already been declared
> ecrivain="Emile Ajar" // mais on peut la mettre a jours
'Emile Ajar'
>
Si une variable n'existe pas (ie. elle n'a pas été
déclarée), lui donner une valeur la déclare
automatiquement.
Mais, (surtout dans les fonctions) ça ne fait pas ce que l'on pense
(cela crée une variable globale dont on reparlera au
moment des fonctions)
On rencontrera souvent le mot clef (historique) var
pour déclarer une variable:
Mais il vaut mieux ne plus l'utiliser (même si je le ferais parfois par
habitude ou à propos) car sont utilisation peut être source de bug. On
en reparlera à la fin des fonctions.
Une variable peut changer de type.
L'opérateur typeof
permet de connaître le type au
moment de l'appel
Plutôt que de compter avec les conversions implicite on préférera des conversions explicites:
// > Number("33") // chaîne vers nombre
33
> String(33) // nombre vers chaîne
'33'
> Number("deux") // impossible de convertir
NaN // on renvoi un Not a Number
> Boolean("true") //chaîne vers Booléen
true
> typeof Number("33")
'number'
> Number("33cm")
NaN
> parseInt("33cm") // plus souple que Number ignore ce qui suit les nombres
33
> parseInt("trente trois") // mais NaN si pas de nombre !
NaN
>
L’opérateur d'affectation =
qui associe
une valeur (résultat de l'evaluation d'une expression)
n'a rien à voir ni avec le =
utilisé en maths ni avec les opérateurs de
comparaisons ===
et ==
. Il n'est pas
symétrique.
=
+=
*=
-=
etc/)> let i=3; // on déclare et initialise i avec la valeur 3
undefined
> i=4 // on met i à jours; la nouvelle valeur est renvoyée
4
> i // on demande la valeur contenue dans i
4
> i+=5 // on ajoute 5 a la valeur contenue dans i et on stock
9 // le résultat dans i; la nouvelle valeur est renvoyée
> i // on demande la valeur contenue dans i
9
> i-=2 // on enlève 2 a la valeur contenue dans i et on stock
7 // le résultat dans i; la nouvelle valeur est renvoyée
> let s="une chaîne" // on déclare et initialise i avec la valeur "une chaîne"
undefined
> s+=" la suite" //on concatène la valeur contenue dans s et la chaîne "la suite"
'une chaîne la suite' //la nouvelle valeur est renvoyée
> s // on demande la valeur contenue dans s
'une chaîne la suite'
> s*=" mais pas avec * sur les chaines" // l'opérateur `*`nest pas défini sur les chaîne
NaN // javascript essai de les convertir en nombre
// ce n'est pas possible il renvoi un NaN
// la * par un NaN produit une NaN
//(comme toute operation sur un NaN)
> let s2="2" // on déclare et initialise s2 avec la valeur "2"
undefined // elle est de type string
> let i2=s2*s2 // on déclare et initialise 2 avec la valeur
undefined // le résultat de l'evaluation de s2*s2.
// comme `*`n'existe pas sur le chaines.
// javascript convertie la valeur de s2 (la chaîne "2")
// vers le nombre 2, puis évalue la multiplication
> i2 // i2 vaut maintenant 4
4
>
++
ou --
):i++
augmente i
de 1 et renvoi
l'ancienne valeur de i;
++i
augmente i
de 1 et renvoi la
nouvelle valeur de i;
idem pour --
Le code JavaScript est une suite d'instructions, (certaines peuvent être des des affectations, ou des déclarations de fonctions ...)
Les instructions sont séparées par des passages à la ligne ou par le
caractère ;
On suggère d'utiliser systématiquement
;
, mais le passage à la ligne reste
toujours un séparateur
On peut écrire :
ou
ou
La dernière forme est la plus lisible et plus robuste à des réorganisation de code.
Et comme les interprètes javascript donne généralement la ligne de la premiere erreurs, il vaut mieux minimiser le nombre d'instruction par ligne.
Si vous tester dans le navigateur vous pouvez remplacer
console.log
par alert
.
Les accolades {
}
délimitent des
blocs et ne sont pas suivies de ;
On distingue les EXPRESSIONS et les INSTRUCTIONS, qui toutes deux vont contenir des variables. L'evaluation d'une expression calcule une valeur, tandis que l'évaluation d'une instruction exécute le code correspondant qui a un effet sur l’environnement (crée des variables, modifie la valeurs des variables, affiche à l'écran, écrit sur une socket reseau ...) !
let a = 3 ; // instruction dont l'execution, declare
//a et l'initialise avec la valeur 3
2*a*a; // expression évalué à la valeur 18
a=2*a; // instruction qui évalue l'expression 2*a
//et met a jours a avec la valeur en résultant
console.log("a vaut ",a); // instruction qui affiche a vaut 6
Les instructions renvoient soit la dernière expression évaluée durant
leur execution soit undefined
.
Le travail interactif au toplevel est pratique pour de petits calculs et pour se familiariser avec javascript. Mais un « vrai » programme, ce n’est pas ça!!
Un programme = une suite d’instructions qui sont exécutées sans interruption et sans afficher de résultats. Il faut forcer l’affichage en utilisant l’instruction operation de sortie.
On appelle sortie la possibilité d'écrire généralement à l'écran ( mais pas que ).
On appelle entrée la possibilité de lire généralement au au clavier ( mais pas que ).
alert
: la fonction historique elle pop une
fenêtre avec votre message. Mais plutôt a éviter en terme
d'IHM.console.log
: affiche le message dans la consoleconsole.log
qui affiche a
l'écranCompliquées...
args
(on verra plus tard
!)prompt-sync
prompt
pour lire des
données> const prompt = require('prompt-sync')();
undefined
> let nom=prompt('entrez un nom :');
entrez un nom :olivier
undefined
> nom
'olivier'
>
Dans la plupart des exemples du début du cours (excepté quand on
étudiera la manipulation du DOM, ou l’asynchronisme), nous
utiliserons prompt
pour lire au clavier et
console.log
pour écrire à l’écran.
console.log
dans le navigateur
dans node
Comme on a nommé des valeurs avec la notion variable .
La notion de fonction va nous permettre de nommer un enchaînement d'instructions pour pouvoir le réutiliser plus tard.
Pour augmenter l’intérêt de la chose ce bloc d'instruction peut être paramétré
On peut définir des fonctions :
On utilise le mot clé return
pour renvoyer un
résultat (ou quitter la fonction sans rien renvoyer).
Pour utiliser la fonction on l'appel en lui passant ces éventuels
paramètres entre parenthèses (
et )
séparés par
des virgules ,
Il n'y a pas d'evaluation partielle, si on ne
fourni pas assez de paramètre alors les paramètres manquants vaudront
juste undefined
Si on passe trop de paramètre, les paramètres excédentaires sont ignorés
le type d'une fonction est function
function volumeSphere(R){
return (4/3)*Math.PI*R*R*R;
}
> volumeSphere(9)
3053.628059289279
>
> volumeSphere()
NaN
> volumeSphere(1,2,3,4)
4.1887902047863905
> typeof volumeSphere
'function'
>
function saluer(nom){
const salutation="Bonjour "+nom;
console.log(salutation)
}
> saluer("olivier")
Bonjour olivier
undefined
> saluer()
Bonjour undefined
undefined
> saluer("olivier","pons")
Bonjour olivier
undefined
>
Depuis ES6 il existe une autre syntaxe pour les fonction, la notation fléchée plus proche de la notation mathématique
On verra au moment des objets que leur sémantique n'est pas tout a fait la même mais d'ici la elles sont interchangeable.
Les fonction sont des valeurs comme les autres en javascript.
On peut les manipuler, les passer en argument ou les renvoyer comme
résultat mais nous y reviendront...
L'instruction conditionnelle if
permet de prendre une
décision.
La forme générale est la suivante:
La condition doit s'évaluer en une valeur booléen.
Si elle est vrai on évalue le premier bloc
Sinon, s'il y a un else
et un second bloc on évalue le
second bloc.
Les parenthèses autour de la condition condition
sont obligatoires. La branche else { ... }
est optionnelle.
Les accolades sont optionnelles pour les blocs d'une seule instruction
mais il faut mieux les mettre (ca permet de rajouter des affichage de
debug sans avoir à rajouter des accolades...)
Si a condition ne s'évalue pas a une valeur booléen, il peut y avoir des conversions implicites
Le if
précédent est une instruction. Il choisi le bloc
d'instruction à executer mais ne renvoie pas de valeur. (meme si la
boucle REPL affiche la valeur de la dernière expression évaluée) Il ne
peut donc servir de membre droit à une affectation :
> let v;
undefined
> v=if(c){a="zero"} else{a="un"}
v=if(c){a="zero"} else{a="un"}
^^
Uncaught SyntaxError: Unexpected token 'if'
> v=if(c){"zero"} else{"un"}
v=if(c){"zero"} else{"un"}
^^
Uncaught SyntaxError: Unexpected token 'if'
>
Pour cela on peut définir des Expressions
Conditionnelles
Dont la forme générale est la suivante:
Ici encore La condition doit s'évaluer en une valeur booléen.
Si elle est vrai la valeur de l'expression est celle qui suit le
?
et precede de :
Si elle est fausse la valeur de l'expression est celle qui suit
:
> let pval=(15>5)?99:1; // 15>3 s'évalue a true donc c'est 99 qui est évalué
undefined // et c'est 99 qui est affecté a pval
> pval;
99
> ((pval >2 && pval <100)?100:200)+33; //(pval >2 && pval <100) s'évalue a true
133 // donc l'expression conditionnelle s'évalue a 100
// on ajoute 33 pour obtenir 133
> let qval=(9<=1)?"valeur1":"valeur2"; // idem avec des chaine
undefined
> qval;
'valeur2'
> (99%4===0)?"quatre divise 99":"quatre ne divise pas 99"
'quatre ne divise pas 99'
> let nbPersonne=3;
undefined
> "il y a "+nbPersonne+" personne"+((nbPersonne>1)?"s":"") //une utilisation classique
'il y a 3 personnes'
>
> (Math.random()>=0.5)?1000:99 //utilisation de random dans la condition, cela choisit un nombre entre O et 1
1000 // donc 1 chance sur 2 d'être inférieure à 0.5
> (Math.random()>=0.5)?"chaine":99 //note: les 2 valeurs possibles peuvent être de type different
99 //ici string et number
//mais c'est souvent source d'erreur et il faut mieux éviter en général
switch
Elle peut dans certains cas remplacer une série (souvent peu
élégante) de if … else
. On l'utilise lorsque les cas à
gérer sont nombreux.
> var reponseHttp=300;
> //....
> switch(reponseHttp){
case 200: console.log("Tout va bien !");break;
case 300: console.log("Vous allez ailleurs !");break;
case 400: console.log("Hum... Vous êtes perdu ?");break;
case 500: console.log("Harg ! Quelque chose a mal tourné !");break;
default : console.log("Heuu... Ce n'est pas un code correct");break;
}
Vous allez ailleurs !
Si on ne met pas une instruction break
,
le script exécutera les instructions pour le cas correspondant
et aussi celles pour les cas suivants jusqu'à
la fin de l'instruction switch ou jusqu'à une instruction break.
>switch(reponseHttp){
case 200: console.log("Tout va bien !");
case 300: console.log("Vous allez ailleurs !");
case 400: console.log("Hum... Vous êtes perdu ?");
case 500: console.log("Harg ! Quelque chose a mal tourné !");
default : console.log("Heuu... Ce n'est pas un code correct");
}
Vous allez ailleurs !
Hum... Vous êtes perdu ?
Harg ! Quelque chose a mal tourné !
Heuu... Ce n'est pas un code correct
undefined
Les boucles sont utilisées pour répéter une suite
d’instructions. La façon la plus générale d’écrire une boucle
en Javascript est d’utiliser for
ou while
dont
la syntaxe est hérité du langage C.
## boucle for à la C
La forme générale est la suivante:
for(initialisations ; condition ; incrémentations) {
//corps de la boucle for
//ie. instructions a répéter
}
> for (let i = 0; i < 6; i = i + 1) {
console.log(i);
}
//on a initialise i a 0 , i est inférieur a 6 donc rentre dans le corps
0 // et on affiche i (donc 0). on increment i qui vaut maintenant 1
1 //i est inférieur a 6 donc rentre dans le corps et on affiche i (1) et on increment i qui passe a 2
2 //i est inférieur a 6 donc rentre dans le corps et on affiche i (2) et on increment i qui passe a 3
3 // ...
4
5 //i est inférieur a 6 donc rentre dans le corps et on affiche i (6) et on increment i qui passe a 6
// n'est pas inférieur a 6 donc on sort de la boucle.
> let somme=0
undefined
> for (let i = 0; i <= 10; i = i + 1) {
... somme+=i;
... }
55
>
Il y a deux types de boucle while
dont les formes
générales sont les suivantes. Dans la seconde forme on passe au moins
une fois dans le corps de la boucle.
while ( condition ) {
//corps de la boucle while
//pensez a faire varier la condition...
}
do {
//corps de la boucle do ...while
// on y passe au moins une fois
//pensez a faire varier la condition...
} while (condition);
let i=0;
while (i<10){
console.log(i);
i=i+1; // Attention si on l'oubli c'est la boucle infinie...
}
let age;
let reponse=false;;
do{
age=prompt("entrez votre nom ?");
reponse=prompt("age :"+age+" est correct ?")
} while(reponse != "oui") // tant que l’utilisateur n'a pas rentre oui
//on boucle
La boucle for
est a priori plus conçu pour les répétions
dont donc on connaît le nombre mais le choix de l'utilisation de l'une
ou l'autre des boucles reste avant tout une affaire de style.
On veut que nos programme terminent donc il faut a priori
éviter les boucles infinies !
(sauf si on écrit un serveur ou un interpréteur comme la boucle
REPL...)
Une autre façon d'obtenir des répétitions est l'écriture de fonctions récursive.
On parle de fonction recursive lorsqu'une fonction s'appelle
elle même
(directement ou en appelant une fonction qui l'appelle).
Pour évitez la boucle infinie, il faut prévoir un cas d'arrêt et 'évolué' vers ce cas a chaque nouvel appel. La forme générale ressemble à ceci:
function maFonctionRecursive(paramètres){
if(cas d'arrêt){return uneValeur} // cas de base
else{
....
return maFonctionRecursive(nouveauxParamètres) // appel récursif
}
}
Considérons
sommeCarres(n)=
\(1+2^2+3^2\dots+n^2\)
En regroupant les (n-1)
premiers carrés de droite on
obtient la relation de recurrence:
sommeCarres(n)=
sommeCarres(n-1)+\(n^2\)
avec la convention que pour n
\(=0\) on a sommeCarres(n)
\(=O\)
Ce qui en javascript s'écrit:
function sommeCarres(n){
if(n===0) {return 0} // cas d'arrêt
else {return (n*n)+sommeCarres(n-1)} // appel récursif
}
function mouvement( de, vers) {
console.log("Déplace un disque de la tige "+de+" à la tige "+vers);
}
function hanoi (depart, milieu, arrivee,n){
if(n>0){
hanoi(depart,arrivee, milieu, (n - 1));
mouvement(depart, arrivee);
hanoi(milieu, depart, arrivee, (n - 1));
}
}
Et avec 3 disques...
> hanoi("A","B","C",3)
Déplace un disque de la tige A à la tige C
Déplace un disque de la tige A à la tige B
Déplace un disque de la tige C à la tige B
Déplace un disque de la tige A à la tige C
Déplace un disque de la tige B à la tige A
Déplace un disque de la tige B à la tige C
Déplace un disque de la tige A à la tige C
undefined
FIN FONCTIONS SIMPLES |
Le moyen le plus simple de représenter une collection de valeur.
ils représentent une séquence finie d'éléments auxquels on peut accéder efficacement par leur position, ou indice, dans la séquence.
[
et ]
> tab[0]="zero" //ajout dans la premiere case
'zero' //d'indice 0
> tab[1]="un" //ajout dans la case d'indice 1
'un'
> tab[2]="deux"
'deux'
> tab
[ 'zero', 'un', 'deux' ]
>
,
.[
et ]
undefined
length
length-1
Attention
> let s= 0;
undefined
> for (i= 0; i < tab3.length; i++) {
s= s + tab3[i];
}
'0azety'
> s;
'0azety'
>
let tabNum=[2,4,6,8,10];
function sommeAux(t,n){
if (n<0) {
return 0
}else{
return t[n]+sommeAux(t,n-1);
}
}
function somme(t){return sommeAux(t,t.length-1)}
On verra des méthodes spécifiques aux tableaux plus tard.
join
: l'argument de join est inséré entre les
éléments.tab= ["un", "deux", "trois"];
tab.push("quatre"); // renvoie le nouveau `length`
// tab[3] vaut maintenant "quatre"
inversement, pop() supprime le dernier élément du tableau, et le retourne:
Voir aussi shift
et unshift
,
slice
Pour supprimer des éléments dans un tableau, on peut utiliser la
méthode splice
. t.splice(indice,nombre)
Supprimer nombre éléments dans t à partir de l'indice indice.
En option :
t.splice(indice,nombre, e1, e2,...)
; supprime nombre
éléments dans t à partir de l'indice indice et insert e1, e2...
Les fonctions d’ordre supérieur sont des fonctions dont les arguments ou les résultats sont eux-mêmes des fonctions. Une fonction d’ordre supérieur est parfois appelée une fonctionnelle
On dit que les fonction sont des objet de premiere classe du langage
Considérons l'archetype des fonctions récursives, la fonction factorielle.
Une récurrence évidente permet d'écrire~:
function fact(n){
if (0===n){
return 1; /* cas de base */
}else {
return n*fact (n-1); /* cas de récurrence */
}
}
Détaillons l'évaluation de fact (3)
fact (
-> if n=0 then 1 else n*fact (n-1) avec la liaison n=3
-> n*fact (n-1) avec la liaison n=3
-> 3*fact (2)
->3 * ( if n=0 then 1 else n*fact (n-1)) avec la liaison n=2
->3 * ( n*fact (n-1)) avec la liaison n=2
->3 * ( 2 * (fact (1)))
->3 * ( 2 * ( if n=0 then 1 else n*fact (n-1))) avec la liaison n=1
->3 * ( 2 * ( n*fact (n-1) )) avec la liaison n=1
-> 3 * ( 2 * (1 *(fact 0)))
-> 3 * ( 2 * ( 1 * ( if n=0 then 1 else n*fact (n-1)))) avec la liaison n=0
-> 3 * ( 2 * ( 1 * 1))
<- 3 * ( 2 * ( 1))
<- 3 * 2
<-6
On constate que la multiplication 3*fact (2)
ne peut
être faite avant de connaitre le résultat de (fact 2)
;
puis que pour évaluer ( 2 * fact (1))
on doit attendre de
connaître le résultat de (fact 1)
; et enfin que la
multiplication (1 *fact (0))
ne peut être faite qu'après le
calcul de (fact 0)
; on peut alors remonter en cascade
(c'est les flêches <-) les multiplications restées en attente. Pour
pouvoir effectuer cette remontée, il faut donc avoir stocké les
opérations en attente. Ce stockage nécessite l'intervention d'une
pile. On remarque par ailleurs que le nombre de
résultats à stocker est proportionnel à l'argument n
donc
que plus cet argument est grand plus on doit stocker de résultats sur la
pile. Dans certains cas, ce stockage peut saturer la mémoire [^]
factIt 3
->(aux 3 1)
-> match p with 0 -> acc |_-> aux (p-1) (acc * p) avec p=3 et acc =1
->aux 2 3
->match p with 0 -> acc |_-> aux (p-1) (acc * p) avec p=2 et acc =3
->aux 1 6
->match p with 0 -> acc |_-> aux (p-1) (acc * p) avec p=1 et acc =6
-> aux 0 6
->match p with 0 -> acc |_-> aux (p-1) (acc * p) avec p=0 et acc =6
->6
Ainsi le calcul de aux(3,1)
se ramène à celui de
aux(2,3) qui lui même se ramène à aux(0,6)
qui renvoie
6
. Les résultats intermédiaires sont stockés dans
l'accumulateur acc
et à chaque étape le nouveau résultat
intermn
.
Dans ce second type de réursivité, l'appel récursif n'est pas enveloppé (c'est-à-dire qu'il n'est pas argument d'une opération) , il est "en position terminale".
** Existence d'une enveloppe $\Leftrightarrow$ "récurrence profonde" $\Leftrightarrow$ pile $\Leftrightarrow$ Mémoire nécessaire dépendant de l'argument**
** Pas d'enveloppe $\Leftrightarrow$ "récurrence terminale" $\Leftrightarrow$ pas besoin de pile $\Leftrightarrow$ M<E9>moire nécessaire indépendante de l'argument}
Toute fonction récursive terminale peut s'écrire de façon itérative.
Bien que la norme prevois l'optimisation de la récursivité terminale. Les implantations courantes (a l’exception de Safari)
On la notion de tableau associatif permet d'associer une clef à une valeur.
En javascript le type n'existe pas c'est un cas particulier d'utilisation des objets.
var dico= {};
dico["java"]= "langage de programmation orienté objet";
dico["lisp"]= "langage de programmation fonctionnel";
Autre méthode de création :
var t= {"java": "langage de programmation orienté objet",
"lisp": "langage de programmation fonctionnel"};
Pour supprimer un élément dans un tableau associatif :
javascript delete t["java"];
## boucles classiques et recursion ## fonctions spécifiques aux tableaux
ce sont des objets
constantes déclarées entre "..." ou entre '...'.
ont une propriété, length
(leur longueur)
on accèdes à leur i<sup>em</sup>
caractère par chaine[i-1]
de nombreuses méthodes:
charAt(pos)
retourne une String (synonyme de
s[pos]
)charCodeAt(pos)
retourne le code unicode du caractère (si inférieur à 65535)"bonjour"[1]; // o
var c="monde"; c.charAt(0) // m
"ABC".charCodeAt(0) // 65
String.fromCharCode(65) // 'A'
//si > 65535 plusieurs codet
var code="😀".charCodeAt(0) // 65
String.fromCharCode(code) // pas "😀"
let hex = "😀".codePointAt(0).toString(16)
let emo = String.fromCodePoint("0x"+hex);
console.log(hex, emo); // affiche 1f600 😀
split
(séparateur) : renvoie le
tableau obtenu en découpant la chaîne.let s= "un,deux,trois";
let t= s.split(","); // t est un tableau de 3 éléments
indexOf(sousChaine,index)
, indexOf(sousChaine): premier
indice de la sous-chaîne, à partir de l'index donné, ou -1.s= "un,deux,trois";
i= s.indexOf("tr"); // i vaut 8
substr(debut,longueur)
: renvoie la sous-chaîne
démarrant à "debut", de longueur "longueur".
toLowerCase()
: renvoie la chaîne en minuscules
;
toUpperCase()
: renvoie la chaîne en
majuscules.
...
Deux fonctions de conversion sont particulièrement intéressantes: +
parseInt(CHAINE)
: analyse chaîne comme un entier. Renvoie
la valeur NaN (not a number) en cas d'échec. +
parseFloat(CHAINE)
: analyse chaîne comme un entier.
Renvoie la valeur NaN en cas d'échec.
Notez que ces deux fonctions sont très permissives. Elles acceptent des chaînes qui commencent par un nombre, et, s'il y a du texte après, elle l'ignorent.
parseFloat("23.56px") //23.56
Pour tester si elles ont réussi, on utilisera la fonction
isNaN()
.
s="1234";
i= parseInt(s);
if (isNaN(i)) {
console.log(s + " n'est pas un nombre");
}
Notes: 1. On peut ajouter un paramètre de base.
javascript parseInt("1101",2); //13 en binaire
2. On
peut utiliser Number
qui est moins permissif
javascript Number("456.4"); Number("23px")
3. On
peut (c mal ! ;-) utiliser les conversions automatiques liés à la
multiplication *
javascript "12345" * 1; // 12345
La structure de données de base est l'objet:
{} //Un objet vide
{ x : 1, y : 2 } //Un objet avec deux champs x et y.
o.x //Accès à un champ
o['x'] //Syntaxe alternative
o.z = 3; //rajoute le champ z à l'objet o !
Les [Objet standards natifs] (http://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects)
(Math,Date,etc.) # Boucle le retour...
Pour parcourir les indices d'un tableau, on peut utiliser
for (let i in t)
t= ["un", "deux", "trois"];
for (let i in t) {
console.log( "case "+i + " : " + t[i]);
}
t= ["un", "deux", "trois"];
for(let x of t){
console.log(x) //un deux trois
}
t= {"java": "langage de programmation orienté objet",
"lisp": "langage de programmation fonctionnel"};
for (let clef in t) {
console.log("information sur le langage " + clef + ": " + t[clef]);
}
un nom de fonction est une variable comme une autre.
On peut définir des fonction anonymes
function next(){ let x=0 (function (){} }
On peut les passer en argument
document
et le DOML'objet global document
représente le document HTML. Il
implémente l'interface DOM et on peut donc le parcourir comme un arbre
(propriétés firstChild, parent, nextSibling ...).
document.getElementById("foo")
permet de
récupérer un objet représentant l'élément HTML de la page ayant
l'attribut id valant "foo"
(null
si cet
élément n'existe pas)Les éléments HTML (document ou les objets renvoyés par
getElementById
) implémentent l'interface DOM du W3C
(Document Object Model, une spécification pour faire correspondre des
concepts HTML sur des langages Objets). Les méthodes qui vont nous
intéresser pour le TP :
foo.addEventListener("event", f)
: Exécute la fonction f
quand l'évènement "event" se produit sur l'élément foo (ou un de ses
descendants).
foo.innerHTML = "<b>Yo !</b>"
: Remplace
tout le contenu de l'élément foo par le fragment de document contenu
dans la chaîne de caractère.
foo.value
: Permet de modifier ou récupérer la valeur de
certains éléments (en particulier les zones de saisies de texte).
foo.style
: Accès au style CSS de l'objet, représenté comme
un objet Javascript
Les navigateurs Web se programment de manière évènementielle : on attache des fonctions (event handlers) à des éléments. Quand un certain évènement se produit, la fonction est appelée :
//On suppose qu'on a un évènement <div id="toto" >
//dans notre HTML
let divToto = document.getElementById("toto");
function toggle_pink() {
if (divToto.style.background == "") {
divToto.style.background = "pink";
} else {
//s'il est déjà en rose, on l'efface et le
//style global reprend le dessus
divToto.style.background = "";
} }
toto.addEventListener("click", toggle_pink);
Dans le slide précédant, il est bien écrit :
toto.addEventListener("click", toggle_pink); et non pas
toto.addEventListener("click", toggle_pink()); //ARCHI FAUX
addEventListener
attend deux arguments : le nom de
l'évènement et une fonction. On appelle cette fonction un gestionnaire
d'évènements
click
: on clique sur l'élément (marche sur tous les
éléments, pas seulement les boutons)
mouseenter/mouseleave
: on survole un élément
graphique ou on le quite avec la souris input : sur les champs de texte,
déclanché quand on saisit du texte
keydown/keyup
: on enfonce ou relache une touche du
clavier
Le gestionnaire d'évènement reçoit un argument e en paramètre qui un objet décrivant l'évènement :
let div = document.getElementById("mondiv");
function clavier (e) {
console.log("On a pressé la touche " + e.key);
}
div.addEventListener("keydown", clavier);
En Javascript, le code est exécuté par le navigateur Web lors de la phase de rendu de la page Web
Si une fonction Javascript prend du temps à s'exécuter, elle bloque la page
Si du code Javascript bloque la page, le navigateur propose à l'utilisateur d'interrompre le script
Le code Javascript a donc une structure très particulières :
Il peut être utile de répéter du code à intervalle régulier (par exemple pour effectuer un animation). (et évidemment, on ne fait JAMAIS d'attente active, i.e. une boucle qui ne fait « rien » et qui exécute du code tous les X tours)
Javascript propose la fonction setInterval
: //on
réutilise la fonction toggle_pink de tout à l'heure
let timer_id = setInterval(toggle_pink, 3000);
//la fonction est appelée toutes les 3000 ms
setInterval
est un identifiant du
« timer » et permet d'arrêter la répetition: clearInterval(timer_id);
//la fonction toggle_pink arrête d'être appelée
Dans le slide précédant, il est bien écrit :
et non pas
Le modèle d'exécution de Javascript est séquentiel :
Deux fonctions Javascript ne peuvent jamais s'exécuter en même temps, même si les évènement associés se produisent exactement au même moment
Le moteur d'exécution Javascript place tous les gestionnaires d'évènements dans une file d'attente et les exécute l'un après l'autre.
On peut utiliser `console.log(e)ù n'importe où dans du code pour afficher la valeur de l'expression e dans la console de débuggage Javascript.
Dans l'ancien temps (2000 ?), les gens faisaient des choses comme ceci :
<html lang="fr">
<head>...</head>
<body>...
<button id="b1" onclick="f()">Cliquez Moi</button>
<input id="t2" type="text" oninput="x = 42; g(y);" />
...
<body>
<html>
.js
--------- exemple de boucle dans un navigateur --------- x = prompt('dis-moi oui, humain!') while (x != 'oui') { x = prompt('dis-moi oui, humain!') } console.log('merci!')
PARTIE ENTREE SORTIE |
console.log
F12
)alert
innerHTML
, ou méthodes du DOM)
dans le navigateur:
alert
pour la
saisie<label for="mon_entre">Entre 1</label>
<input type="text" id="mon_entre1">
on récupère la chaîne saisie en javascript
let mavariable=document.getElementById("mon_entre").value
dans node en récupérant les argument **en ligne de commande
Si on a le fichier exempleArgvNode.js
suivant
const process = require('process');
// il faudrait évidement tester ne nombre d'arguments
console.log("chemin de node ",process.argv[0]);
console.log("chemin du programme ",process.argv[1]);
console.log("premier argument ",process.argv[2]);
console.log("second argument ",process.argv[2]);
et qu'on lance le programme dans un terminal en lui passant des argument
pons$ node exempleArgvNode.js argument1 argument2 ...
on a
chemin de node /usr/local/bin/node
chemin du programme /Users/pons/Documents/GIT/COURS/cours/NFA041/codefile/exempleArgvNode.js
premier argument argument1
second argument argument1
//le module prompt-sync doit être installé
//sinon "npm install prompt-sync" le terminale terminal
```javascript
const prompt = require('prompt-sync')();
let nom = prompt('quel est votre nom?');
console.log("bonjour ",nom);
FIN DE LA PARTIE SPECIALE ENTREE/SORTIE |
null
et undefined
null
est une constante spéciale, de type
object
(dont on parlera plus loin).undefined
est une constante spéciale, de type
undefined
; Correspond à la valeur d'une variable non
initialisée ou d'une propriété non existante pour un objet.