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 :

This faisant référence au mauvais objet


Sujet :

JavaScript

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Mars 2011
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Mars 2011
    Messages : 5
    Points : 7
    Points
    7
    Par défaut This faisant référence au mauvais objet
    Bonjour,

    J'ai récemment décidé de me mettre à la "POO" avec Javascript. J'utilise donc le prototypage, mais j'ai un soucis au niveau du code suivant (version simplifiée de mon code) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var maClasse = function(cadreCible){
    	this.cadre = cadreCible;
    	this.boite = this.add();
    }
     
    maClasse.prototype = {
    	add : function(){
    		// Divers opérations dont celle ci-dessous
    		monBouton.addEventListener('click',this.remove,false);
    	}
    	remove : function(){
    		this.cadre.removeChild(this.boite);
    	}
    }
    Lorsque je créé une instance de cette classe, je passe en argument un cadre (div), que j'affecte à la propriété cadre. La méthode add est alors appelée, ajoute des éléments au DOM, et renvoie l'élément principal ajouté à la propriété boite.

    Dans la méthode add, je fais appel à un addEventListener pour executer une fonction au clic sur un bouton.

    Le soucis est le suivant : dans la méthode remove, this ne fait plus référence à ma classe mais à monBouton.

    Sauriez vous comment je pourrais faire référence aux propriétés cadre et boite dans la méthode remove ?

    Je vous remercie d'avance.

  2. #2
    Membre confirmé

    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 311
    Points : 545
    Points
    545
    Par défaut
    Salut !

    En JavaScript utilisation du mot clef this dans une fonction, référence l’instance de l’objet qui maintien la référence sur cette fonction.

    Je m’explique, le gestionnaire d’événement du bouton garde une référence sur ta fonction remove, c’est donc le bouton qui appelle la fonction remove, changeant ainsi le context d’appelle , this référence donc le bouton .

    Il existe plusieurs méthodes pour sauvegarder le context d’appelle (this) , personnellement j’utilise le prototypage de fonction pour y ajouter une mécanique de delegate, comme ce ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Function.prototype.Delegate = function(obj)
    {
        var method = this;
        return function() { return method.apply(obj, arguments); };
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var maClasse = function(cadreCible){
    	this.cadre = cadreCible;
    	this.boite = this.add();
    }
     
    maClasse.prototype = {
    	add : function(){
    		// Divers opérations dont celle ci-dessous
    		monBouton.addEventListener('click',this.remove.Delegate(this),false);
    	},
    	remove : function(){
    		this.cadre.removeChild(this.boite);
    	}
    }

  3. #3
    Rédacteur

    Avatar de Bovino
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2008
    Messages
    23 647
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2008
    Messages : 23 647
    Points : 91 220
    Points
    91 220
    Billets dans le blog
    20
    Par défaut
    Une autre technique consiste à conserver la référence à this dans une variable :
    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
    var maClasse = function(cadreCible){
    	var me = this;
    	this.cadre = cadreCible;
    	this.boite = this.add();
    }
     
    maClasse.prototype = {
    	add : function(){
    		// Divers opérations dont celle ci-dessous
    		monBouton.addEventListener('click',me.remove,false);
    	},
    	remove : function(){
    		this.cadre.removeChild(this.boite);
    	}
    }
    Sans oublier de mettre une virgule entre les membres de l'objet prototype

  4. #4
    Invité
    Invité(e)
    Par défaut
    La dernière ne marchera pas, me n'est pas dans le même scope. Par contre l'idée est la même :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function add(){
     var me = this;
     monBouton.addEventListener('click',me.remove,false);
    }

  5. #5
    Expert confirmé
    Avatar de RomainVALERI
    Homme Profil pro
    POOête
    Inscrit en
    Avril 2008
    Messages
    2 652
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : POOête

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 652
    Points : 4 164
    Points
    4 164
    Par défaut
    Moi c'est plutot ce passage qui me chiffonne >>> this.boite = this.add(); .

    Dans la mesure où la fonction add ne retourne aucune valeur... pourquoi vouloir stocker "rien" dans une propriété de l'objet ?
    (si c'est parce que tu voulais à la fois créer cette propriété tout en laissant une valeur "undefined" ET que tu as voulu "gagner" une ligne, ne me l'avoue surtout pas ça va me faire du mal )

  6. #6
    Membre confirmé

    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 311
    Points : 545
    Points
    545
    Par défaut
    Citation Envoyé par galerien69 Voir le message
    La dernière ne marchera pas, me n'est pas dans le même scope. Par contre l'idée est la même :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function add(){
     var me = this;
     monBouton.addEventListener('click',me.remove,false);
    }
    Desolé mais cela ne resou pas le probleme initial !
    le branchement de la méthode remove a l'evenement "Click" du bouton ne pose aucun problème ... mais c'est lors de l’exécution de la méthode remove que le problème survient !

    tu doit confondre avec la technique de la fonction "anonyme" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function add()
    {
    	var me = this;
    	monBouton.addEventListener('click',
    	function () // remove en fonction anonyme
    	{
    		me.cadre.removeChild(me.boite);
    	},
    	false);
    }
    pour moi cette technique ressemble plus a un Hack car on perd la mutualisation de la méthode remove!

    l'autre technique viable, dont parlé Bovino, est de sauvegarder le context this dans une variable de la fonction contructeur
    mais contrairement a son exemple il faut tous déclarer dans le contructeur,meme les methodes, et bannir l'utilisation du mot clef this
    comme ceci :
    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
     
    var maClasse = function(cadreCible)
    {
    	var me = this;
    	// mot clef this est bannit a partir d'ici ...
    	me.add = function()
    	{
    		// Divers opérations dont celle ci-dessous
    		monBouton.addEventListener('click',me.remove,false);
    	};
     
    	me.remove = function()
    	{
    		me.cadre.removeChild(me.boite);
    	};
     
    	me.cadre = cadreCible;
    	me.boite = me.add();
     
    }
    cette technique a l'avantage de garantir que me reference bien un type maClasse
    mais ne support pas le prototypage

  7. #7
    Invité
    Invité(e)
    Par défaut
    Desolé mais cela ne resou pas le probleme initial !
    Comme tu las dit le probleme survient sur lexec du callback de onclick.

    Lorsque add est appelée, on peut oser espérer que c'est sur maClasse.
    Donc me=this pointe bien sur maClasse.

    A fortiori lorsque me.remove est appelé, this réfère me, qui réfère bien maClasse.

  8. #8
    Membre expérimenté Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Points : 1 519
    Points
    1 519
    Par défaut
    Vous êtes fous !

    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
    var maClasse = function(cadreCible){
    	this.cadre = cadreCible;
    	this.boite = this.add();
    };
     
    maClasse.prototype = {
    	add : function(){
    		// Divers opérations dont celle ci-dessous
    		var me = this;
    		monBouton.addEventListener('click',function(){me.remove()},false);
    	},
    	remove : function(){
    		this.cadre.removeChild(this.boite);
    	}
    };

    ou plus largement

    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
    function bind(fn,obj){
    	return function(){return fn.apply(obj,arguments)};
    }
    //-----------------------------
    var maClasse = function(cadreCible){
    	this.cadre = cadreCible;
    	this.boite = this.add();
    };
     
    maClasse.prototype = {
    	add : function(){
    		// Divers opérations dont celle ci-dessous
    		monBouton.addEventListener('click',bind(this.remove,this),false);
    	},
    	remove : function(){
    		this.cadre.removeChild(this.boite);
    	}
    };
    edit: je n'avais pas vu la réponse de p3ga5e (dans le post #2) qui est tout aussi correcte. (sauf qu'il faut remplacer "call" par "apply").

  9. #9
    Expert confirmé
    Avatar de RomainVALERI
    Homme Profil pro
    POOête
    Inscrit en
    Avril 2008
    Messages
    2 652
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : POOête

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 652
    Points : 4 164
    Points
    4 164
    Par défaut
    Citation Envoyé par Willpower Voir le message
    Vous êtes fous !


    @threonine : ça va ? y'a de quoi faire, là... tu t'en sors ? ^^

  10. #10
    Membre confirmé

    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 311
    Points : 545
    Points
    545
    Par défaut
    Citation Envoyé par galerien69 Voir le message
    Lorsque add est appelée, on peut oser espérer que c'est sur maClasse.
    Donc me=this pointe bien sur maClasse.
    Exacte mais le contraire n’est pas vrais ! Lors de l’exécution de la fonction remove this != me.
    En EcmaScript this est redéfinit a chaque exécution, il peut pointer n’importe quel type …
    soit le développeur détermine l’objet que this référencera grâce aux méthodes call et apply
    sinon il référence l’objet qui maintient la référence sur la fonction … ce n’est pas très claire , un petit exemple pour illustrer mes propos :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var f = function()
    {
         alert(this.Name);
    };
     
    f(); // this devrait etre egale undefined , sur les navigateurs this = window
    var refA = { Name : "A" , Display : f};
    var refB = { Name : "B" , Display : f };
    var refC = { Name : "C" , Display : f };
    refA.Display(); // this = refA
    refB.Display(); // this = refB
    f.call(refC); // this = refC
    Citation Envoyé par Willpower
    edit: je n'avais pas vu la réponse de p3ga5e (dans le post #2) qui est tout aussi correcte. (sauf qu'il faut remplacer "call" par "apply").
    Tu as parfaitement raison j’edit mon 1er post pour corrigé cela !

    Citation Envoyé par RomainVALERI
    @threonine : ça va ? y'a de quoi faire, là... tu t'en sors ? ^^
    Je pense que l’on a réussi à le dégouter, définitivement, du Javascript

  11. #11
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2011
    Messages
    442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2011
    Messages : 442
    Points : 417
    Points
    417
    Par défaut
    p3ga5e, pourquoi ça marche pas ça ? Po compris

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function add(){
     var me = this;
     monBouton.addEventListener('click',me.remove,false);
    }
    edit : si, ça y est, je crois que j'ai compris : addEventListener va executer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this.cadre.removeChild(this.boite);
    donc monBouton.addEventLIstener va executer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    monBouton.cadre.removeChild(monBouton.boite);
    c'est ça?

  12. #12
    Membre confirmé

    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 311
    Points : 545
    Points
    545
    Par défaut
    Oui ! enfin presque !

    La fonction remove n’est pas appelé lors du addEventListener, mais lors d’un click utilisateur, addEventListener ne fait que sauvegarder la référence de la fonction remove.

    Mais le problème reste le même c’est bien monBouton qui appel la fonction remove donc this = monBouton lors de l'evenment click !

  13. #13
    Invité
    Invité(e)
    Par défaut
    Et boum!

    toussa pour économiser un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    function(){me.remove()}
    , ca m'apprendra (dans les deux sens...)

  14. #14
    Membre expérimenté Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Points : 1 519
    Points
    1 519
    Par défaut
    Citation Envoyé par Sharcoux Voir le message
    p3ga5e, pourquoi ça marche pas ça ? Po compris

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function add(){
     var me = this;
     monBouton.addEventListener('click',me.remove,false);
    }
    edit : si, ça y est, je crois que j'ai compris : addEventListener va executer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this.cadre.removeChild(this.boite);
    donc monBouton.addEventLIstener va executer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    monBouton.cadre.removeChild(monBouton.boite);
    c'est ça?
    Exacte ! (enfin comme dit p3ga5e, c'est pas addEvent qui va exécuter mais l’évènement lui-même)

    alors qu'avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    function(){me.remove();/*this==monButton*/}
    le this vaudra de nouveau "monBouton" mais peu importe puisqu'on ne l'utilisera pas et qu'on utilisera "me"(objet de maClasse) à la place. Et on appelera donc la méthode remove sur cette objet. et donc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this.cadre.removeChild(this.boite);
    sera équivalent à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    me.cadre.removeChild(me.boite);
    avec me l'objet(de maClasse) qui a appelé "add" initialement.


    edit:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    monBouton.addEventListener('click',me.remove,false);
    ne passe que la méthode "remove" comme argument, peu importe d'où tu la sors, le contexte ne joue que lors d'un appel(exécution).

    c'est exactement comme si tu faisais :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    monBouton.addEventListener('click',function(){this.cadre.removeChild(this.boite);},false);
    puisque
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    me.remove == maClasse.prototype.remove == function(){this.cadre.removeChild(this.boite);}

Discussions similaires

  1. [WD15] Valeur faisant référence à un objet
    Par Pascal26120 dans le forum WinDev
    Réponses: 0
    Dernier message: 27/04/2010, 13h08
  2. [POO] Perte de la référence sur mon objet (this) lors d'un évènement
    Par muad'dib dans le forum Général JavaScript
    Réponses: 5
    Dernier message: 20/12/2008, 12h59
  3. Référence d'un objet
    Par tsp dans le forum Langage
    Réponses: 2
    Dernier message: 07/06/2006, 12h48
  4. Références croisées d'objets Oracle
    Par cdemedei dans le forum Oracle
    Réponses: 2
    Dernier message: 23/02/2006, 16h33
  5. Réponses: 13
    Dernier message: 12/09/2005, 09h09

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