IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

Chrono JS: passer du procédural en POO


Sujet :

JavaScript

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2020
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2020
    Messages : 119
    Par défaut Chrono JS: passer du procédural en POO
    Bonjour, je viens vous solliciter car j'ai besoin d'aide.

    Je dois faire un compte à rebours de 20 minutes en javascript. Je propose ce code qui fonctionne en procédural :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    startTimer = (duration, display) => {
        var timer = duration;
        var minutes;
        var seconds;
        setInterval(function () {
            minutes = parseInt(timer / 60);
            seconds = parseInt(timer % 60, 10);
     
            minutes = minutes < 10 ? "0" + minutes : minutes;
            seconds = seconds < 10 ? "0" + seconds : seconds;
     
            display.textContent = minutes + ":" + seconds;
     
            if (--timer < 0) {
                timer = duration;
            }
        }, duration);
    }
    Et j'appel cette fonction ailleurs, dans une méthode appartenant à ma class Reservation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    Dom.getValider().addEventListener('click', () => {
         let twentyMinutes = 60 * 20, display = document.getElementById('time');
         startTimer(twentyMinutes, display, 1000);
    });
    En gros , lorsque je clique sur le bouton valider, la fonction startTimer se déclenche, le décompte de 20 minutes est lancé jusque 0.

    Mais ça se complique lorsque je transforme ce code en POO :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class Chrono {
        constructor(twentyMinutes) {
            this.twentyMinutes = twentyMinutes;
            this.timer = this.duration, 
            this.minutes,
            this.seconds;
            this.timeleft = document.querySelector('#time');
        }
     
        startTimer(duree) {
     
            setInterval(function () {
                this.minutes = parseInt(this.timer / 60);
                this.seconds = parseInt(this.timer % 60, 10);
     
                this.minutes = this.minutes < 10 ? "0" + this.minutes : this.minutes;
                this.seconds = this.seconds < 10 ? "0" + this.seconds : this.seconds;
     
                this.timeleft.textContent = this.minutes + ":" + this.seconds;
     
                if (--this.timer < 0) {
                    this.timer = this.duration;
                }
            }, duree);
        }
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    Dom.getValider().addEventListener('click', () => {
         const myChrono = new Chrono(60 * 20);
         myChrono.startTimer(1000);
    });
    Je pense avoir bien fait, je ne comprends pas. J'obtiens ceci dans la console lorsque je clique sur valider :
    Nom : chrono.png
Affichages : 640
Taille : 1,8 Ko
    (içi, cela correspond à chrono.js:19)

    On dirait que le chrono est enclenché (d'ou le numéro qui change , içi avec 8, 9, 10, etc) mais il n'arrive pas à lire ce qu'il y a après le "textContent".
    Je ne comprends pas car cela marche en procédural. J'ai juste changé l'affichage du chrono , non plus dans le display mais dans ma balise #time (this.timeleft).

    Merci par avance de m'aider car je bute sur ça depuis ce matin...

  2. #2
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 198
    Par défaut
    Bonjour,
    tu as le même problème que celui rencontré dans pas mal de discussions, c'est lié à la valeur du this lorsque la fonction dans ton setInterval est exécutée, mets un console.log(this) juste pour voir
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    startTimer(duree) {
        // sauve le contexte
        const thisObj = this;  
     
        setInterval(function () {
            // juste pour voir et comprendre 
            console.log("this :", this);
            console.log("thisObj :", thisObj);
     
            this.minutes = parseInt(this.timer / 60);
            this.seconds = parseInt(this.timer % 60, 10);
     
            this.minutes = this.minutes < 10 ? "0" + this.minutes : this.minutes;
            this.seconds = this.seconds < 10 ? "0" + this.seconds : this.seconds;
     
            this.timeleft.textContent = this.minutes + ":" + this.seconds;
     
            if (--this.timer < 0) {
                this.timer = this.duration;
            }
        }, duree);
    }

  3. #3
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2020
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2020
    Messages : 119
    Par défaut
    Merci pour ta réponse. J'ai trouvé un code beaucoup plus simple pour moi :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class Chrono {
        constructor(minutes, secondes) {
            this.minutes = minutes;
            this.secondes = secondes;
            this.a;
        }
     
       startTimer() {
            // sauve le contexte
            const thisObj = this;
            setInterval(function () {
                // juste pour voir et comprendre 
                console.log("this :", this);
                console.log("thisObj :", thisObj);
                this.a = new Date();
                Dom.getTime().innerHTML = this.minutes + ":" + this.secondes;
                this.secondes--;
                if (this.secondes == 0) {
                    this.minutes--;
                    this.secondes = 60;
                    if (this.minutes == 0) {
                        this.minutes = 2;
                    }
                }
            }, 1000);
        }
    }
    Cette fois-ci, je n'ai aucun message d'erreur lorsque je clique sur valider, mais , au lieu d'avoir le décompte (20:00, 19:59, 19:58, etc) j'ai à la place undefined: undefined, puis undefined : NaN .

    Je rappel que j’exécute ce code au moment du clique sur le bouton "Valider" dans une autre class
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Dom.getValider().addEventListener('click', () => {
         const myChrono = new Chrono(19, 59);
         myChrono.startTimer();
    J'ai essayé console.log(this) et cela me met ceci :

    > this : Window
    > thisObj : Chrono > minutes: 19
    secondes: 59
    __proto__: Object


    > this : Window*{parent: Window, opener: null, top: Window, length: 0, frames: Window,*…}
    Mais cela ne m'aide pas plus que ça. lorsque je déroule cette ligne, j'ai énormément de choses mais je ne pense pas que cela soit utile.

    Pour infos j'ai ceci dans ma class Dom ( qui est positionné avant toute mes class)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    class Dom {
        constructor() {
        }
     
        static getCanvas() {
            return document.getElementById('canvas');
        }
     
        static getValider() {
            return document.querySelector('.valider');
        }
     
        static getAnnuler() {
            return document.querySelector('.annuler');
        }
     
        static getTime() {
            return document.querySelector('#time');
        }
     
        static getStations() {
            return document.getElementById('info-station');
        }
     
        static getEtat() {
            return document.getElementById('status');
        }
     
        static getNom() {
            return document.getElementById('name');
        }
     
        static getAdresse() {
            return document.getElementById('address');
        }
     
        static getVeloDispo() {
            return document.getElementById('available_bikes');
        }
     
        static getPlaceDispo() {
            return document.getElementById('bike_stands');
        }
    }
     
    const myStation = new Dom();
    On retrouve bien la variable time. Donc je ne comprends pas pourquoi javascript n'arrive pas à lire ce qu'il y a après .innerHTML :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Dom.getTime().innerHTML = this.minutes + ":" + this.secondes;

    Vraiment je préfère programmer en procédural car je trouve cela plus simple mais on m'oblige de ne faire que de la POO

  4. #4
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 198
    Par défaut
    J'ai essayé console.log(this) et cela me met ceci :
    ...
    > this : Window
    > thisObj : Chrono > minutes: 19
    ...
    Mais cela ne m'aide pas plus que ça. lorsque je déroule cette ligne, j'ai énormément de choses mais je ne pense pas que cela soit utile.
    cela te dit que la fonction startTimer est appelée dans le contexte thisObj, à savoir ta classe Chrono et que la fonction exécutée dans ton setInterval est exécutée dans le contexte window.

    En clair tu cherches à modifier la variable secondes appartenant à window et celle-ci n'existe pas, incrémenter une variable qui vaut undefined ne fait rien de bon

    Remplace tes this.nomVariable par thisObj.nomVaraible et regarde ce qui se passe.

    PS :
    au passage ta Class Dom ne vaut pas grand chose et l'utilisation que tu en fais est incorrecte, mais c'est une autre histoire !

  5. #5
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2020
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2020
    Messages : 119
    Par défaut
    Merci en effet je ne sais pas encore très bien interprêter les messages de la console de ce genre je suis relativement nouveau en javascript. J'étudie ça demain matin merci

  6. #6
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2020
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2020
    Messages : 119
    Par défaut
    PS :
    au passage ta Class Dom ne vaut pas grand chose et l'utilisation que tu en fais est incorrecte, mais c'est une autre histoire !
    Enfait j'ai deja vu plusieurs étudiant faire cela. sauf que moi j'ai fait des getters (comme mon mentor m'a conseillé) de manière à pouvoir utiliser ces variables partout. En quoi cette méthode est incorrect ?

  7. #7
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 198
    Par défaut
    En quoi cette méthode est incorrect ?
    ce n'est pas tant la méthode, je parle de la déclaration, qui est incorrecte mais la façon dont tu l'utilises.

    Lorsque tu écris const myStation = new Dom() on s'attend à ce que tu utilises myStation, mais comme les méthodes sont statiques tu ne peut pas, elles ne sont visibles que par Dom.

    Le plus gros défaut que j'y trouve est qu'à chaque appel tu refais un document.getElementById, ou autres, et qu'à chaque fois que tu vas définir un nouvel élément dans le DOM tu vas ajouter une méthode, là c'est très moyen.
    Il faut factoriser et mettre en cache tes éléments.

    Je ne sais si je dois aller plus loin dans ce sens ?

    Nota au passage, il me semble que je t'avais fournie une astuce lors de ta dernière discussion, pour rappe c'est icil.

    Une autre remarque au passage concernant le fait de faire confiance au délai de setTimeout, il vaut mieux faire une contrôle avec la date/heure en cours tu auras une meilleur garantie du délai réel, fais des tests en changeant de page et en y revenant.

  8. #8
    Membre émérite
    Femme Profil pro
    Autre
    Inscrit en
    Janvier 2017
    Messages
    340
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Janvier 2017
    Messages : 340
    Par défaut
    Bonjour,
    Citation Envoyé par NoSmoking Voir le message
    cela te dit que la fonction startTimer est appelée dans le contexte thisObj, à savoir ta classe Chrono et que la fonction exécutée dans ton setInterval est exécutée dans le contexte window.
    J'ai un peu de mal avec cette formulation (à moins que ce ne soit pour être mieux compris ?) qui est pourtant assez en accord avec la documentation française, mais peut-être moins avec l'anglaise.

    Je mets deux passages en comparaison :
    Français :
    https://developer.mozilla.org/fr/doc...%A9rateur_this
    1) "Valeur
    L'objet JavaScript représentant le contexte..."

    2) "...les fonctions fléchées dans lesquelles this correspond à la valeur du contexte englobant."

    Anglais :
    https://developer.mozilla.org/en-US/...Operators/this

    1) "Value
    A property of an execution context..."

    2) "it retains the this value of the enclosing lexical context"

    La formulation anglaise me paraît plus précise, voire plus exacte.

    Citation Envoyé par Lunesti Voir le message
    Donc si j'ai bien compris la fonction setInterval s'éxécutait dans le contexte window et non dans le contexte de la class Chrono()
    La formulation me dérange.
    Déjà ce n'est pas la fonction setInterval qui nous préoccupe, mais plutôt celle qu'on lui a mise comme premier argument.

    Je pense qu'il y a deux concepts importants pour mieux comprendre :
    - Contexte d'exécution, en lien avec la valeur de this.
    - Environnement lexical, en lien avec la portée des variables.


    Quand on exécute la fonction startTimer, on entre dans un nouveau contexte d'exécution.
    A ce moment, this est défini selon la façon dont a été appelée cette fonction.
    Quand on appelle cette fonction en faisant myChrono.startTimer(1000); alors this correspondra à l'objet myChrono.
    Il existe d'autres façons d'appeler cette fonction pour lesquelles this pourrait avoir une autre valeur.

    Ensuite, la fonction setInterval elle-même (pas son premier argument) est appelée depuis ce contexte d'exécution.
    Son exécution entraînera un nouveau contexte d'exécution.
    Mais peu importe ici la mécanique exacte de la fonction setInterval.

    Enfin, concernant la fonction mise en premier argument de setInterval :
    Quand elle sera exécutée, on entrera dans un nouveau contexte d'exécution et, encore une fois, this sera défini selon la façon dont est appelée cette fonction.
    Cette fois-ci c'est window et non plus myChrono qui sera choisi pour this.


    Puisque la valeur de this ne nous convient plus, une solution possible (une autre étant les fonctions fléchées) est de se baser sur l'environnement lexical.
    L'important est l'endroit où est définie la fonction et pas celui d'où elle est appelée.
    Or, à l'endroit où est définie notre fonction, on peut ajouter une variable/constante à laquelle on aura alors accès : const thisObj = this;.

  9. #9
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 198
    Par défaut
    Citation Envoyé par Lunesti
    Donc si j'ai bien compris la fonction setInterval s'éxécutait dans le contexte window et non dans le contexte de la class Chrono(),
    Pas exactement c’est
    et que la fonction exécutée dans ton setInterval est exécutée dans le contexte window.
    voir également l'explication de Loralina.


    Citation Envoyé par Loralina
    J'ai un peu de mal avec cette formulation (à moins que ce ne soit pour être mieux compris ?)
    C’est tout à fait cela, on est d’accord sur le fond, il n’est pas toujours aisé de trouver la formulation la plus claire qui soit aussi la plus simple à comprendre, ce mécanisme étant loin d’être intuitif.


    Citation Envoyé par Loralina
    Puisque la valeur de this ne nous convient plus, une solution possible (une autre étant les fonctions fléchées) est de se baser sur l'environnement lexical.
    Les solutions sont nombreuses, « préservation » du contexte, mise en place d’une closure, création d’une fonction dédiée avec utilisation de bind …

    L’utilisation des fonctions fléchées est effectivement la solution la plus simple à mettre en oeuvre sous réserve que l’on n’ait pas a supporté IE.


    @Lunesti :
    Un autre point, tu arrêtes ton timer comment en fin de décomptage ?

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Passer du procédural à la POO
    Par MonsieurPoulet dans le forum Langage
    Réponses: 7
    Dernier message: 19/09/2013, 10h18
  2. Passer une procédure en paramètres
    Par FamiDoo dans le forum Windows Forms
    Réponses: 5
    Dernier message: 17/02/2008, 14h41
  3. Passer des procédures stockées à Hibernate
    Par zizoux5 dans le forum Hibernate
    Réponses: 2
    Dernier message: 11/04/2007, 13h08
  4. Comment passer une procédure en paramètre ?
    Par gudul dans le forum Langage
    Réponses: 4
    Dernier message: 30/09/2005, 13h57
  5. Passer une procédure en paramètre ?
    Par Cornell dans le forum Langage
    Réponses: 2
    Dernier message: 24/10/2003, 12h21

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo