Aller au contenu

[JS] Visibilité d'une variable encapsulée?


serik

Messages recommandés

Salut,

Je me pose une petite question en JavaScript :

function MaClasse {
 this.uneVariable = "lala";

 // ... //
 unElementHtml.onclick = function(){
   alert(this.uneVariable); // marche pas
 }
}

Ce code ne fontionne pas car this référence l'élement HTML. Y a-t-il moyen d'accéder à une variable membre de classe sans passer par une variable externe au code, genre :

var objet = new MaClasse();

function MaClasse {
 this.uneVariable = "lala";

 // ... //
 unElementHtml.onclick = function(){
   alert(objet.uneVariable); // Ok mais crade
 }
}

Je suis ouvert à une modification plus profonde de code, parce que j'ai plusieurs évènements qui portent sur un objet n'étant pas l'élément html.

Merci beaucoup!

Edit : c'est plutôt sur l'accès à des fonctions membres que la question se pose, mais je ne pense pas que ça change grand chose.

Lien vers le commentaire
Partager sur d’autres sites

function MaClasse {
 var uneVariable = "lala";


 unElementHtml.onclick = function(){
   alert(uneVariable);
 };
}

Tout simplement, car le scope en js se gère au niveau des fonctions, et un scope à accès à son scope parent

[handler] ---> [instance MaClasse]

Toute la subtilité résidant dans le var, car une propriété d'un objet n'est pas la même chose qu'un symbole dans un scope.

De plus les scopes et les symboles sont initialisés à la création de l'objet d'activation, ç-à-d avant l'execution du body de ta fonction.

Lien vers le commentaire
Partager sur d’autres sites

Je n'aime pas trop la méthode fournie par Yangzebul car dans le cas d'objets complexes tu te retrouves à faire des

function MaClasse {
 this.uneVariable = "lala";
 var uneAutre = this;

 // ... //
 unElementHtml.onclick = function(){
   alert(uneAutre.uneVariable);
 }
}

le fait de dupliquer un objet dans une variable juste pour pour avoir accès au scope de l'objet c'est laid.... et c'est illisible quand le code devient touffu, bref retour au point de départ.

Tout dépend du contexte du code, je n'ai pas trop le temps de chercher pour le moment mais dans les framework js récent tu as normalement - enfin il me semble - possibilité de binder une fonction à un scope... et donc à l'objet. Je ne sais pas du tout si c'est possible simplement en js pur (une fois qu'on a gouté à un fw...).

Voici le code sous Mootools :

   var class = new Class({
         uneVariable: "lala",
         // ... //
         unElementHtml.set('click', function(){
             alert(this.uneVariable);
             }.bind(this)
      });               

Je pense sans certitude qu'il existe des équivalents sur la plupart des gros fw js.

Au passage déclarer un évènement via une propriété HTML c'est pas super clean non plus, ca fonctionne sur des a mais si tu commences à en mettre sur des éléments plus exotiques tu vas avoir des différences de comportements entre les navigateurs, mieux vaut passer par la déclaration d'un listener (ou mieux, passer par un fw et utiliser un gestionnaire d'évènements)

Lien vers le commentaire
Partager sur d’autres sites

En fait ce n'est pas tant une question de complexité de l'objet que de visibilité :

function Cls(){

   var v = "vvv"; // private
   this.w = "www"; // public
   document.getElementById("monelem").onclick = function(){

       alert(this); // monelem
       alert(this.v); // undefined car this = monelem
       alert(v); // vvv, via la scope chain
   };
}

var inst = new Cls();

alert(inst.v); // undefined car dans le scope de l'instance
alert(inst.w); // www, visible depuis l'extérieur car propriété de l'objet, et non symbole du scope de l'instance

Sinon, oui bind est dispo dans tout les framework, cela consiste à créer une closure sur un scope en encapsulant la fonction dans une autre fonction ( -> closure -> scope chain) qui invoquera la fonction d'origine via la méthode call/apply.

Cela existe même an AS (cf. Delegate).

L'utilisation d'un bind est plus lourde, elle ne devrait être fait que si l'on as besoin d'exécuter le handler sur un autre scope que l'élément auquel est lié l'évènement (modification de la valeur this) ou si le membre de l'objet doit rester public.

Sinon +1 pour le gestionnaire d'évènement, mais bon je suppose que c'était juste pour simplifier l'exemple ou par flemme d'en écrire trop. :transpi:

Lien vers le commentaire
Partager sur d’autres sites

Intéressant...

J'avais jamais fait le rapprochement avec la visibilité d'une variable :transpi:

Pour le reste +1, explication très claire d'ailleurs. Ceci étant dit hors cas très simple comme ceux montrés en exemple plus haut je trouve quand même l'utilisation du bind beaucoup plus lisible et puis en terme de perf... quand on voit la différence d'un même script sur 2 navigateurs différents :reflechis:... et je ne parle pas du comportement :craint:

Lien vers le commentaire
Partager sur d’autres sites

Ok merci pour vos réponses, mais comme je l'ai dit dans mon edit du premier post, ça concerne plus l'appel à une fonction.

Ca concerne des évènement sur un div (pour le onclick) et sur un champ texte (pour onkeydown).

En fait c'est pour une sorte d'auto-complétion.

Je n'utilise pas de fw à cause de leur taille à charger. (et aussi une méconnaissance des fw :zarb: )

Que pensez-vous de ça :

function MaClasse {

 this.methodeX = function(parametre){ // devrait être public
   // fonction qui modifie des propriétés de l'instance de MaClasse
 }

 this.uneAutreMethode = function(){
   var div = document.createElement("div");
   // des opérations sur le div : ajout des éléments d'auto-complétion
   div.MaClasse = this; // une éventuelle solution?
   div.onclick = function(evenement){
     // ici je doit appeler la methodeX avec un parametre dépendant de evenement
     this.MaClasse.methodeX(parametre); // la solution?
   }
 }
}

Le div créé contient les éléments d'auto-complétion.

Le champ texte gère les touches flèches, enter, esc...

Je crée une classe pour la réutilisabilité (essentielle dans ce travail).

What else?

Lien vers le commentaire
Partager sur d’autres sites

Les fonctions étant des objets comme un autre, tout ce que l'on as dit au dessus s'applique à l'identique.

Je te conseille, comme l'as dit Fuinril, de faire un bind.

Si tu ne veux pas utiliser de framework, tu peux toujours écrire ton propre helper pour binder des fonctions à des contextes.

C'est très simple, c'est une fonction qui retourne une fonction englobant ta fonction d'origine, et qui quand elle sera executée fera un call/apply sur l'objet que tu veux en contexte.

Inspire toi des autres librairies, tu verras c'est très formateur, et cela se fait à tout casser en une dizaine de lignes. ;)

Lien vers le commentaire
Partager sur d’autres sites

Rohhh l'autre l'excuse du poids des fw :non:

On l'a tous fait celle là :transpi:

Bon en même temps si tu n'as pas un niveau très avancé en js c'est une bonne chose de faire "from scratch", au moins on comprend un peu mieux ce qui se passe et puis... ca devient tellement vite un gros bordel qu'on en apprécie d'autant plus le fw quand on en découvre un (en plus on sait ce qu'on cherche et ca va donc vachement plus vite à apprendre - avis basé sur mon expérience perso).

Bon c'est un peu hors cadre mais, particulièrement dans le cadre d'une autocomplétion où tu vas générer des nodes qui auront leurs propres évènements, je te conseille très vivement de ne pas utiliser "monDiv.onclick = " mais de passer par un gestionnaire d'évènements, dans le cas contraire attends toi à beaucoup de surprises très chiantes à débuger car ayant un comportement (très) différent selon les navigateur - sans parler du cas ou tu vas penser que le event.target fait référence à un autre objet que celui que tu pensais.

Lien vers le commentaire
Partager sur d’autres sites

Un fw ça pèse combien? 1Mo? Plus? Y a-t-il possibilité de "bytecoder" le code pour gagner de la place?

onClick a un comportement différent sous ie?

Avec addEvent, on remplace ou on ajoute l'évènement? J'ai besoin de killer l'évènement.

Pour les closures, j'ai mis du temps à comprendre comment ça marche exactement, faut dire qu'après 10 ans de C/C++/Java ça déroute un peu! Pour finir j'ai pas besoin de faire un bind, une simple closure suffit.

Autre question : comment créer le tag <br/> en JS?

Question existentielle : y aura-t-il un jour une standardisation générale de JS?

Merci encore.

Lien vers le commentaire
Partager sur d’autres sites

Un fw ça pèse combien? 1Mo? Plus? Y a-t-il possibilité de "bytecoder" le code pour gagner de la place?

Non, on ne peut pas "bytecoder" car tous les interpréteur/vm sont différents et ont leur propre représentation intermédiaire.

Par contre tu peux minimifier + gziper ta librairie.

Pour avoir un ordre d'idée, jQuery (que personnellement je n'aime pas) fait dans les 60k minimifié, et 20k une fois gzippé.

onClick a un comportement différent sous ie?

Oui, car l'API pour déclarer des évènements sous IE est antérieure et différente de l'API DOM standardisée.

Avec addEvent, on remplace ou on ajoute l'évènement? J'ai besoin de killer l'évènement.

attachEvent/addEventListener rajoute sans remplacer. Tu peux avoir autant de callbacks que tu veux sur un même évènement.

Pour détacher un évènement tu as detachEvent/removeEventListener.

Pour inhiber un évènement event.returnValue=false/preventDefault.

Pour stopper la propagation d'un évènement dans l'arbre DOM event.cancelBubble=true/stopPropagation.

Pour les closures, j'ai mis du temps à comprendre comment ça marche exactement, faut dire qu'après 10 ans de C/C++/Java ça déroute un peu! Pour finir j'ai pas besoin de faire un bind, une simple closure suffit.

Cela ne te sera pas inutile, c'est une notion extrèmement importante en JS. Et ce concept a été introduit dans Java 7.

Autre question : comment créer le tag <br/> en JS?

Attention, un tag n'est pas un élément (http://perfectionkills.com/tag-is-not-an-element-or-is-it/)

document.createElement("br")

Question existentielle : y aura-t-il un jour une standardisation générale de JS?

Merci encore.

Il y en as une, mais tout le monde y fait des entorses, et tu as aussi le poids du passé...

Mais honnêtement JS n'est pas plus mal loti qu'un autre. Va changer de VM en Java, ou utiliser une autre implémentation d'une API supposée standard...

Le problème en JS c'est que contrairement aux autres langages tu ne contrôle pas l'environnement d'exécution, tu ne compiles pas, et tu n'as pas la main sur les API. Donc les problèmes de cohérence que tous les autres langages/environnements ont mais que tu évites, là tu te les prends en plein dans les dents, et tu es obligé de faire avec. :craint:

Lien vers le commentaire
Partager sur d’autres sites

attachEvent/addEventListener rajoute sans remplacer. Tu peux avoir autant de callbacks que tu veux sur un même évènement.

Pour détacher un évènement tu as detachEvent/removeEventListener.

Pour inhiber un évènement event.returnValue=false/preventDefault.

Pour stopper la propagation d'un évènement dans l'arbre DOM event.cancelBubble=true/stopPropagation.

Mais si j'utilise addEventListerner sur une ancre, alors l'ancre va quand même se charger? Comment virer l'évènement par défaut?

Cela ne te sera pas inutile, c'est une notion extrèmement importante en JS. Et ce concept a été introduit dans Java 7.

Java 7 n'est pas finalisé il me semble. Et puis, avec le rachat de sun par oracle, je sens que je vais aller voir ailleurs (et surtout que java ne sait pas gérer les caractères unicodes supérieurs à 0xFFFF, ce qui me pose un problème...)

Autre question : comment créer le tag <br/> en JS?

Attention, un tag n'est pas un élément (http://perfectionkills.com/tag-is-not-an-element-or-is-it/)

document.createElement("br")

Le problème c'est que createElement("br") donne "<br>" alors qu'en xHTML c'est "<br/>" qu'il faut.

Le navigateur comprend quand même, mais la page n'est plus "strictement" valide.

Autres questions :

- Il n'existe pas de fonction sleep/wait, y a-t-il un moyen de se débrouiller sans définir une fonction qui prendrait eb paramètre une fonction de callback et qui ferait des "setTimeout"? (en clair qu'on fasse "fonctionattente(1000)", et que la fonction ne retourne la main qu'une fois l'attention finie).

- Comment enregistrer la page une fois qu'elle a été modifiée par le code JS?

Merci & bonne journée!

Lien vers le commentaire
Partager sur d’autres sites

Mais si j'utilise addEventListerner sur une ancre, alors l'ancre va quand même se charger? Comment virer l'évènement par défaut?

Tu ne peux pas vraiment, par contre tu peux ne pas en définir (exemple : ne pas mettre d'attribut href sur un a ). L'autre solution, la plus couramment utilisée, c'est de produire un return false sur ton evenement :

   div.addEeventListener('click', function(){aleter 'toto';return false;}, false);

Java 7 n'est pas finalisé il me semble. Et puis, avec le rachat de sun par oracle, je sens que je vais aller voir ailleurs (et surtout que java ne sait pas gérer les caractères unicodes supérieurs à 0xFFFF, ce qui me pose un problème...)

Pas vraiment de rapport avec java ici, il te disait juste que la notion de bind existait aussi en java, mais on parle bien de javascript.

Autre question : comment créer le tag <br/> en JS?

Le problème c'est que createElement("br") donne "<br>" alors qu'en xHTML c'est "<br/>" qu'il faut.

Le navigateur comprend quand même, mais la page n'est plus "strictement" valide.

Tu confonds 2 choses qui sont d'ailleurs très bien expliquées dans le lien de Yangzebul : un tag et un élément. En fait le xHTML c'est quoi ? C'est une norme de parsing qui dit entre autre choses "La bonne manière de déclarer un élément c'est de l'ouvrir et de le fermer", donc effectivement (<br></br> ou <br/>, ce qui revient au même). Lorsque tu crées un element DOM via javascript tu ne modifies PAS le code source de ta page, tu modifies la structure, donc il n'y a pas de notion "createElement("br") donne "<br>"", il y a seulement la notion j'ai ajouté un élément br dans mon arbre DOM. La confusion provient sans doute de la souplesse des navigateurs qui sont capable de retourner un code source équivalent à l'arbre DOM sur demande, donc effectivement

   div.appendChild(document.createElement('br'));
   alert(div.innerHTML)

retourne bien "<br>", mais l'arbre dom est en réalité "div element -> br element". J'ai peur de dire des bêtises parceque ça fait longtemps que je n'ai pas fait ça, mais il me semble qu'un excellent moyen de se rendre compte de cela est d'essayer de passer un select dans un innerHTML sous IE6 avec des options variables

  div.innerHTML = "<select>";
  for(var i=0; i<sommeArray.length; i++)
     div.innerHTML += "<option>"+sommeArray[i]+"</option>";
  div.innerHTML += "</select>";

Ce qui génère de mémoire un beau "<select></select><option>1</option><option>2</option><select></select", et démontre bien par la même occasion que du code généré en javascript est bien parsé et interprété par le navigateur, puisque pour le coup le html est parfaitement valide.

- Il n'existe pas de fonction sleep/wait, y a-t-il un moyen de se débrouiller sans définir une fonction qui prendrait eb paramètre une fonction de callback et qui ferait des "setTimeout"? (en clair qu'on fasse "fonctionattente(1000)", et que la fonction ne retourne la main qu'une fois l'attention finie).

Non, pas de moyens propres autre que le setTimeout. Tu peux magouiller des trucs (une requete ajax synchrone par exemple, ou un while (date<temp); ), mais ca sera crade - et dans le deuxieme cas ca va faire très mal à l'UC de tes visiteurs. La seule temporisation existante sur du js pur c'est le setTimeout (a savoir tout de même que setTimeout appelé sans le deuxieme argument (timer) va attendre que les traitements précédent (ex : creation d'elements) soit terminés avant d'exécuter la fonction, c'est peu connu et TRES pratique - sauf en ajax bien sûr, dans ce cas se débrouiller avec les état XHR). Mais de vrais sleep, désolé.

- Comment enregistrer la page une fois qu'elle a été modifiée par le code JS?

C'est très bête ce que je vais dire, mais javascript n'a pas vraiment vocation à faire ça.... c'est un langage qui sert justement à faire des modifications dynamiques. La seule méthode que je puisse voir c'est de récupérer le innerHTML de ton body et de l'enregistrer dans un fichier, mais comme tu l'as toi même constaté le rendu HTML d'un arbre dom modifié est à prendre avec des pincettes. Pour la suite tu l'enregistres dans un fichier en js directement (bonne chance parce que c'est compliqué et en plus t'as énormément de chance d'être pris pour un script malveillant et bloqué) ou tu l'envoies au serveur qui se charge de l'enregistrement.

Merci & bonne journée!

Merci, de même

Lien vers le commentaire
Partager sur d’autres sites

attachEvent/addEventListener rajoute sans remplacer. Tu peux avoir autant de callbacks que tu veux sur un même évènement.

Pour détacher un évènement tu as detachEvent/removeEventListener.

Pour inhiber un évènement event.returnValue=false/preventDefault.

Pour stopper la propagation d'un évènement dans l'arbre DOM event.cancelBubble=true/stopPropagation.

Mais si j'utilise addEventListerner sur une ancre, alors l'ancre va quand même se charger? Comment virer l'évènement par défaut?

Tu ne peux pas vraiment, par contre tu peux ne pas en définir (exemple : ne pas mettre d'attribut href sur un a ). L'autre solution, la plus couramment utilisée, c'est de produire un return false sur ton evenement :

    div.addEeventListener('click', function(){aleter 'toto';return false;}, false);

Si si c'est possible : pour prévenir l'évènement par défaut il faut faire... "preventDefault()". Comme je l'ai indiqué dans ma réponse originale.

Le problème c'est que createElement("br") donne "<br>" alors qu'en xHTML c'est "<br/>" qu'il faut.

Le navigateur comprend quand même, mais la page n'est plus "strictement" valide.

On se moque totalement que ce soit un "<br>" "<br/>" ou même un "[br]".

Ta page a déjà été parsée, quand tu fais tes manipulations DOM tu travaille sur un AST (Abstract Syntax Tree) la représentation textuelle retournée par ton navigateur ne dépends que des conventions internes de son parser. Cela n'a aucun impact sur la validité de ta page. La seule manière que tu aurais de compromettre la validité de ta page via le DOM serait d'attacher un élément à un parent illégal (ex : un bloc dans un inline), mais tu n'as pas à t'inquiéter de la syntaxe, tu es au-dela de la syntaxe, tu manipule un élément en mémoire.

Pour le reste, bah Fuinril a déjà très bien répondu.

Bon courage.

Lien vers le commentaire
Partager sur d’autres sites

Archivé

Ce sujet est désormais archivé et ne peut plus recevoir de nouvelles réponses.

×
×
  • Créer...