Programmation Javascript (NFA041)

Feuille 3

Olivier Pons
(pons@cnam.fr)

2022

Exercice 1

Définir des objects javascript pour représenter la pizza « basique » à 8 euros cinquante, dont les ingredients sont «sauce tomate»,«mozzarella» et «jambon», et la pizza « bbq » à 9 euros cinquante, dont les ingredients sont « Râpé » , « oignons » , « poulet» , « oignons » et « sauce barbecue ».

:::solution ### Solution


let piz1=
{
    nom:"basique",
    prix:8.50,
    ingredients:["sauce tomate","mozzarella","jambon" ]
}


let piz2={
    nom:'bbq',
    prix:9.5,
    ingredients:["Râpé","oignons","poulet","oignons","sauce barbecue"]
}

Exercice 2

Proposer une fonction creerPizza pour créer un objet pizza, a partir d'un nom d'un prix et d'une liste d'ingredients.
Puis l'utiliser pour créer une pizza « basquaise » à « 8 euros 90 » dont les ingredients sont « Râpé » « oignons » « poivrons » « poulet ».

Solution

function creerPizza(nom,prix,ingredients){
    return {
        nom:nom,
        prix:prix,
        ingredients:ingredients
    }
}


let piz3=creerPizza("basquaise",8.90,["Râpé","oignons","poivrons","poulet"])

Exercice 3

Proposer un constructeur ou une classe pour les pizzas. Puis l'utiliser pour créer une pizza « frenchy » à « 9 euros » dont les ingredients sont « Râpé » « camembert » « herbes de Provence » « huile d'olive ».

Solution

function Pizza(nom,prix,ingredients){
   this.nom=nom;
   this.prix=prix,
   this.ingredients=ingredients
}

//ne pas oublier le new !!!

let piz4=new Pizza("frenchy",9,["Râpé","camembert","herbes de Provence","huile d'olive"])

ou avec une classe ( sans méthode, autres que le constructeur, on n'ira pas tres loin mais c'est pour voir la syntaxe )

class Pizza {
    nom
    prix
    ingredients
    constructor(nom,prix,ingredients){
    this.nom=nom,
    this.prix=prix,
    this.ingredients=ingredients
    }
}


let piz4=new Pizza("frenchy",9,["Râpé","camembert","herbes de Provence","huile d'olive"])

Exercice 4

Une commande de pizza, a un nom, une adresse et la liste des pizzas commandées On peut lui ajouter des pizzas, ou demander le prix total.

Proposer une classe pour représenter une commande.

Solution

class Commande{
    constructor(nom,adresse,liste){
    this.nom=nom;
    this.adresse=adresse
    this.liste=liste||[] //si pas de liste alors tableau vide
    }
    ajouter(pizza){
        this.liste.push(pizza);
    }
    calculerPrix(){
        let res=0;
        for(let i=0;i<this.liste.length;i++){
            res+=this.liste[i].prix
        }
        return res;
    }
}



>  let commande1=new Commande("pons","paris",[piz1,piz2])
undefined
> commande1.calculerPrix()
18
> 

Exercice 5

Vous voulez garder la trace des livres que vous avez lus et de ceux que vous voulez lire.

Pour cela, créez un tableau d'objets livre , où chaque objet décrit un livre et possède 3 propriétés le titre , l'auteur et déjàLu (un booléen indiquant si vous l'avez déjà lu).

Solution

let  meslivres = [
  {titre: 'Cours de calcul différentiel',
   auteur: 'Henri Cartan',
   deja: false
  },
  {titre: 'Analyse hilbertienne',
  auteur: 'Laurent Schwartz ',
  deja: false
  },
  {titre: 'Algèbre linéaire et géométrie élémentaire',
  auteur: 'Jean Dieudonné ',
  deja: true
  },
  {titre: 'Théorie algébrique des nombres ',
  auteur: 'Pierre Samuel ',
  deja: true
  }
  ];


function afficheLivre(livres){
    for (let i = 0; i < livres.length; i++) {
        let livre = livres[i];
        let infoLivre = '"'+livre.titre + '" de ' + livre.auteur;
        if (livre.deja) {
            console.log('Vous avez déjà lu ', infoLivre);
        } else {
            console.log('Vous devez encore lire',  infoLivre);
        } 
    }
}
afficheLivre(meslivres)

Pour aller plus loin, vous pouvez aussi définir une classe livre et une classe listeDeLivre, ajouter une méthode qui sépare les livres lus des autres et produit 2 listes, etc..

Exercice 6

Il s'agit de définir un objet chronometre avec deux méthodes. - demarrerqui démarre le chronomètre - arreter qui l’arrête et renvoie le nombre de millisecondes écoulées depuis le démarrage.

Si le chronomètre n'est pas démarré elle ne fait rien.
Sinon il attend qu'on lui demande de s’arrêter.
Quand on l’arrête, il affiche le temps écoulé depuis le démarrage

On pourra utiliser Date.now()qui renvoie le nombre de millisecondes depuis le 1er Janvier 1970.

>chronometre.start()
undefined
> chronometre.end() // presque 2 seconde après
1943   
> 

Solution


let chronometre={
    estDemmarre:false,
    current:undefined,
    demarrer : function (){ this.estDemmarre=true; this.current=Date.now(); },
    arreter : function (){ if(this.estDemmarre){
        //sinon on ne fait rien
        this.estDemmarre=false;
        return Date.now()-this.current; }
        }

}

On peut l'utiliser pour comparer le temps d'execution de 2 fonctions. il suffit de comparer les resultat de :

chronometre.start();f1();chronometre.end()
chronometre.start();f2();chronometre.end()

Exercice 7

Écrire une fonction pour convertir un objet en une liste de paires.

let p={nom:"Marx",prenom:"Karl"}

> objet2List2paires(p)
[ [ 'nom', 'Marx' ], [ 'prenom', 'Karl' ] ]

Solution

function objet2List2paires(o){
    let res=[];
    for (let key in o){ 
        res.push([key,o[key]]);
    }
    return res;
}

Exercice 8

L'objectif est ici de modéliser (de façon rudimentaire) un compte en banque. Dans premiere version le compte un compte a juste un montant et deux méthode, ajouter et retirer qui font varier le montant.
On ne peut retirer plus que le montant present sur le compte.

Solution

Avec une classe

class Compte {
    montant
    // Constructeur
    constructor(montantDeDepart) {
        this.montant=montantDeDepart ||0;  //0 si pas d'argument
    }

    // Une méthode qui crédite le compte 
    ajouter(somme) {
        this.montant = this.montant + somme;
    return this.montant
    }
    // une methode pour retirer de l'argent
    retirer(somme){
        //on verifie qu'il y a assez
        if(somme > this.montant){console.log("retrait max :"+this.montant);
            return
            }
        this.ajouter(- somme)
        return this.montant
    }
}

c=new Compte(100);

Avec une fonction de creation

let baseCompte={
        ajouter: function(somme) {
            this.montant = this.montant + somme;
            return this.montant
        },
    // une methode pour retirer de l'argent
        retirer: function (somme){
            //on verifie qu'il y a assez
            if(somme > this.montant){
                console.log("retrait max :"+this.montant);
                return
            }
            this.ajouter(- somme)
            return this.montant
        }
    };
    
function creerCompte(montantDeDepart){
    let o=Object.create(baseCompte)
    o.montant=montantDeDepart||0;
    return o;
    }

d=creerCompte(50);

Exercice 9

Définir une classe CompteAvecDécouvert qui hérite de compte mais qui autorise un découvert d'un montant maximum qui peut être fixer par une méthode fixerDécouvert

Solution

class CompteAvecDécouvert extends Compte {
    decouvert
    // Constructeur
    constructor(montantDeDepart,decouvert) {
        super(montantDeDepart)
        this.decouvert=decouvert ||0;  //0 si pas d'argument

    }
    fixerDecouvert(somme){
        this.decouvert=somme;
    }
    
    //on redefini retirer
    retirer(somme){
        //on verifie qu'il y a assez
        if(somme > this.montant+this.decouvert){console.log("retrait max :"+(this.montant+this.decouvert));
            return
            }
        //ajouter est celui défini dans compte    
        this.ajouter(- somme)
        return this.montant
    }
}

ou si on crée avec des fonctions:

On peut définir un Compte et le mettre comme prototype du nouvel objet compteAvecDecouvert mais on veut éviter de le faire à chaque creation. Tous les comptes avec decouvert autorisé doivent partager le même prototype.

Pour cela on va creer une fermeture avec une fonction appliquée immédiatement

let  CreerCompteAvecDecouvert= (function (){
    let compte=creerCompte();                  // visible que dans la fonction 
    let proto=Object.create(compte);           // visible que dans la fonction 
    proto.retirer =function (somme){
        //on verifie qu'il y a assez
        if(somme > this.montant+this.decouvert){console.log("retrait max :"+(this.montant+this.decouvert));
            return
            }
        //ajouter est celui défini dans compte    
        this.ajouter(- somme)
        return this.montant
    }
    return function (montant,decouvert){
        o=Object.create(proto);
        o.montant=montant;
        o.decouvert=decouvert;
        return o;
    }

})();

Les variables compte et proto ne sont visibles que dans la fonction CreerCompteAvecDecouvert. Quand cette fonction est appliqué (avec les () à la fin) cela renvoie une fonction anonyme à 2 arguments, dans laquelle compte et proto ont été encapsulées et peuvent être utilisées.

Il n'y a aucun moyen d'acceder a ces variables par ailleurs, mais rien n’empêche de créer d'autre variables compte ou proto proto dans le contexte externe à CreerCompteAvecDecouvert
Elle n'aurtont aucun liens (et donc aucun risque de conflit) avec les variables définies localement dans CreerCompteAvecDecouvert et « encapsulé » par la fonction renvoyée.

Exercice 10

Écrivez une fonction JavaScript pour imprimer toutes les méthodes d'un objet JavaScript. On pourra utiliser getOwnPropertyNames pour obtenir un tableau de de toutes les propriétés d'un objet. Il s'agit de retenir celles qui ont une valeur fonctionnelle.

On peut éventuellement utiliser la fonctionnelle filter pour filtrer les propriétés; on peut aussi faire une boucle.

Solution

function ToutesLesMethods(obj) {
    return Object.getOwnPropertyNames(obj).filter(function(property) {
        return typeof obj[property] == "function";
    });
}


> ToutesLesMethods(Math)
[
  'abs',    'acos',  'acosh',  'asin',
  'asinh',  'atan',  'atanh',  'atan2',
  'ceil',   'cbrt',  'expm1',  'clz32',
  'cos',    'cosh',  'exp',    'floor',
  'fround', 'hypot', 'imul',   'log',
  'log1p',  'log2',  'log10',  'max',
  'min',    'pow',   'random', 'round',
  'sign',   'sin',   'sinh',   'sqrt',
  'tan',    'tanh',  'trunc'
]

ou avec une boucle

function ToutesLesMethods(obj) {
    let res=[];
    let tabMethodes=Object.getOwnPropertyNames(obj);
    for(let i=0;i<tabMethodes.length;i++){
        if(typeof obj[tabMethodes[i]] == "function"){
            res.push(tabMethodes[i])
        }
    }
    return res;
}