Bonjour,

Voila je rencontre un petit problème avec mon code. J'avais fais un post équivalent à l'époque ou je mélangeait l'objet et la class. C'est encore compliqué pour moi aujourd'hui.

Ce que je fais

Voici mon code d'origine qui fonctionne.

var produit = $('.produit');

$(document).ready(function () {
    produit.on('mouseenter', function () {
        $(this).addClass('pulse').delay(1000).queue(function () {
            $(this).removeClass('pulse').dequeue();
        });
    });
});

Ce que je veux

Je veux qu'il soit réutilisable sur une autre page en mettant d'autres paramètres par exemple.

var AnimationCSS = function (element, action, selector) {
    this.element = element;
    this.action = action;
    this.selector = selector;
    this.init();
};

AnimationCSS.prototype.init = function () {
    $(this.element).on(this.action, function () {
        $(this).addClass(selector).delay(1000).queue(function () {
            $(this).removeClass(selector).dequeue();
        });
    });
};

$(document).ready(function () {
    var produit = new AnimationCSS('.produit', 'mouseenter', 'pulse');
});

Ce que j'obtiens

Je rencontre un sacré problème Ligne 9 : ReferenceError: selector is not defined. en faisant des console.log devant la ligne 9. Ma propriété 'selector' n'est plus défini !

console.log(selector); // Non défini !
console.log(this.selector); // La même
console.log(AnimationCSS.selector); // Toujours là même chose, grrrr !

Un problème de Hoisting ?!?

Qui a un chapeau mou et un fouet pour partir "à la recherche de la variable perdu" ?
https://www.youtube.com/watch?v=-bTpp8PQSog

5 réponses


Maenhyr
Réponse acceptée

Salut,
yep, tu crées une nouvelle fonction avec un nouveau contexte ici

$(this.element).on(this.action, function () {

Du coup le this qui est ici est relatif a $(this.element) et non plus à AnimationCSS.

Un petit fix rapide

var AnimationCSS = function (element, action, selector) {
    this.element = element;
    this.action = action;
    this.selector = selector;
    this.init();
};

AnimationCSS.prototype.init = function () {
    var selector = this.selector;
    $(this.element).on(this.action, function () {
        $(this).addClass(selector).delay(1000).queue(function () {
            $(this).removeClass(selector).dequeue();
        });
    });
};

$(document).ready(function () {
    var produit = new AnimationCSS('.produit', 'mouseenter', 'pulse');
});
reuno92
Auteur

Merci de ta réponse, prbaron, j'ai pas tout de suite compris ton explication.

Ce que j'ai compris c'est que le $this est lié au bloc de la fonction mais plus du tout au prototype. Du coup, ce que tu me démontre c'est que tu as crée une variable qui lie $this.selector à la première fonction. Ce faisant on garde le contexte d'origine pour être utilisé plus tard, dans une nouvelle fonction.

Si je vulgarise, avec javascript on perd vite ses variables d'une Fonction dans une autre Fonction.

Du coup, une question légitime se pose à moi : Y a t'il un moyen plus simple (<= Est ce que je rêve un peu, non ?) pour éviter ce genre de problèmes ?

Je checke la réponse, merci encore. J'ai encore envie de faire évoluer mon code du coup et de rajouter un paramètre "Duration" pour le Delay. Après tout, c'est ça développer. :p

Je t'invite à lire ces deux liens pour mieux comprendre comment fonctionne le thisen JS:
https://toddmotto.com/understanding-the-this-keyword-in-javascript/
http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/

Du coup, une question légitime se pose à moi : Y a t'il un moyen plus simple (<= Est ce que je rêve un peu, non ?) pour éviter ce genre de problèmes ?

Avec ES2015, tu as le fat arrow (=>) qui règle ce problème. Sinon tu peux utiliser .call() si tu souhaites rester avec ES5.

reuno92
Auteur

J'ai commencé à lire sur ce que tu m'a envoyé. J'ai repris quelques exemples pour bien comprendre et je suis tombé sur quelque chose qui m'a sur place :) :

console.log('Script témoin');
var numbers = [{
    name: 'Mark'
},{
    name: 'Tom'
},{
    name: 'Travis'
}];
for (var i = 0; i < numbers.length; i++) {
    console.log(this); // window
}

Ici, j'ai essayé de modifier le paramètre du console.log(), avec this.numbers et this.numbers.name. Ca me donne toujours window. Pour arriver à voir les noms de chaque l'opérateur this n'est pas utile car il faut biensur mettre les paramètres numbers[i] et numbers[i].name respectivement pour avoir les objets et les noms.

console.log('1er script');
var numbers1 = [{
    name: 'Mark'
},{
    name: 'Tom'
},{
    name: 'Travis'
}];
numbers1.forEach(function () {
    console.log(this); // window
});

Là, j'ai avec this.numbers1 3x Array avec les 3 objets et avec this.numbers1.name un joli Undefined. Comme je connais très mal forEach(), je suis allé faire un tour sur la doc. et j'ai essayé ceci :

[...]  // je ne récrit pas l'array
numbers1.forEach(function () {
    console.log(this); 
}, this);

Ce qui me revoi toujours à window. Du coup, voulant quand même essayer à retrouver les noms j'ai fais plusieurs tentative pourarrivé à :

[...]  // je ne récrit pas l'array
numbers1.forEach(function (obj, name) {
    console.log(obj.name); 
});

Je retrouve bien mes trois noms. Ok là je comprends this ne sert à rien dans ce cas précis. dans la doc MDN, j'ai bien lu la partie avec le this qu'il puisse être pris en compte il faut qu'il soit en paramètre du forEach() après le callback.

console.log('2e script');
var numbers2 = [{
    name: 'Mark'
},{
    name: 'Tom'
},{
    name: 'Travis'
}];
for (var j = 0; j < numbers2.length; j++) {
    (function () {
        console.log(this.name); // Mark, Tom, Travis
    }).call(numbers2[j]);
}

Voilà le moment où je commence à avoir beaucoup de mal. Le code fonctionne. Mais je ne comprends pourquoi à l'intérieur de la boucle. La fonction que l'on aperçoit. Je la connais très mal. Est ce que c'est ça une fonction recursive ?

Ce que j'en comprends : Une fonction anonyme dans une boucle qui est itéré une fois. Par contre ce qui est dans la fonction est bien pris en compte dans la boucle via call() en prenant bien en compte le paramètre Numbers2[j]. cela voudrait dire donc qui'il est substitué par this.

Ca retourne une fois Numbers2[0].name, Numbers2[1].name, Numbers2[2].name.
Si je comprends bien ca palie au problème des scripts plus hauts qui me renvoyait 3x la même chose...

console.log('3e script');
var numbers3 = [{
    name: 'Mark'
},{
    name: 'Tom'
},{
    name: 'Travis'
}];
numbers3.forEach(function (item) {
    (function () {
        console.log(this.name); // Mark, Tom, Travis
    }).call(item);
});

La même chose que ci-dessus mais avec un tableau, donc évidemment on utilise un forEach avec le paramètre item repris dans le call, être substituer par le this.

Pour le moment, je vais rester sur L'ECMAScript5, pendant un moment. pour bien comprendre les bases et arrivé à me démerder le jour ou je tomberais sur du code à mettre à niveau. Mais je vais garder en tête, le "fat arrow".

Merci encore j'ai appris énormement de choses en 10 heures en te lisant.

console.log('Script témoin');
var numbers = [{
    name: 'Mark'
},{
    name: 'Tom'
},{
    name: 'Travis'
}];
for (var i = 0; i < numbers.length; i++) {
    console.log(this); // window
}

le this global est window, c'est donc normal de l'obtenir ici.

numbers1.forEach(function () {
    console.log(this); 
}, this);

forEach() te permet de définir le contexte (ici le deuxième arguments). Le contexte devient donc ton this à l'intérieur de ta fonction. Or ici tu lui donne le contexte global, c'est à dire window.

Si tu fais ceci :

numbers1.forEach(function () {
    console.log(this); 
}, numbers1);

tu t'apercevras que tu obtiendras l'array complet a chaque fois. C'est parce que tu modifies le contexte a chaque appel et que tu lui donnes l'array.

Si tu veux obtenir le bon comportement, il te faut écrire ceci :

numbers1.forEach(function(item) { // tu itères pour chaque objet dans ton array
  (function() {  // tu crées une nouvelle fonction anonyme
      console.log(this.name);
  }).call(item) // le call te permet de définir le contexte dans la fonction anonyme
});

Honnêtement, à ce niveau, autant écrire :

numbers1.forEach(function(item) {
      console.log(item.name);
});