Portée De Rappel JavaScript

j'ai quelques problèmes avec le Vieux JavaScript (pas de cadres) pour référencer mon objet dans une fonction de rappel.

function foo(id) {
    this.dom = document.getElementById(id);
    this.bar = 5;
    var self = this;
    this.dom.addEventListener("click", self.onclick, false);
}

foo.prototype = {
    onclick : function() {
        this.bar = 7;
    }
};

maintenant quand je crée un nouvel objet (après que le DOM a chargé, avec un essai de#de portée)

var x = new foo('test');

Le' ceci ' à l'intérieur de la fonction onclick pointe vers le test de span#et non vers l'objet foo.

Comment puis-je obtenir une référence à mon objet foo dans la fonction onclick?

58
demandé sur Chris MacDonald 2008-10-08 18:56:09

7 réponses

(extrait d'une explication cachée dans les commentaires dans une autre réponse)

Le problème réside dans la ligne suivante:

this.dom.addEventListener("click", self.onclick, false);

ici, vous passez un objet de fonction à utiliser comme callback. Lorsque l'événement se déclenche, la fonction est appelée mais elle n'a plus d'association avec un objet (ceci).

le problème peut être résolu en enveloppant la fonction (avec sa référence objet) dans un fermeture comme suit:

this.dom.addEventListener(
  "click",
  function(event) {self.onclick(event)},
  false);

puisque la variable self a été assignée cette quand la fermeture a été créée, la fonction de fermeture se souviendra de la valeur de la variable self quand elle est appelée à un moment ultérieur.

une autre façon de résoudre ceci est de faire une fonction d'utilité (et éviter d'utiliser des variables pour lier ce ):

function bind(scope, fn) {
    return function () {
        fn.apply(scope, arguments);
    };
}

le code mis à jour serait alors ressemble à:

this.dom.addEventListener("click", bind(this, this.onclick), false);

Function.prototype.bind fait partie D'ECMAScript 5 et offre la même fonctionnalité. Donc vous pouvez faire:

this.dom.addEventListener("click", this.onclick.bind(this), false);

pour les navigateurs qui ne prennent pas encore en charge ES5, MDN fournit le shim suivant:

if (!Function.prototype.bind) {  
  Function.prototype.bind = function (oThis) {  
    if (typeof this !== "function") {  
      // closest thing possible to the ECMAScript 5 internal IsCallable function  
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");  
    }  

    var aArgs = Array.prototype.slice.call(arguments, 1),   
        fToBind = this,   
        fNOP = function () {},  
        fBound = function () {  
          return fToBind.apply(this instanceof fNOP  
                                 ? this  
                                 : oThis || window,  
                               aArgs.concat(Array.prototype.slice.call(arguments)));  
        };  

    fNOP.prototype = this.prototype;  
    fBound.prototype = new fNOP();  

    return fBound;  
  };  
} 
81
répondu hishadow 2011-12-22 22:34:27
this.dom.addEventListener("click", function(event) {
    self.onclick(event)
}, false);
15
répondu Sergey Ilinsky 2011-01-30 04:07:23

pour les jQuery utilisateurs cherchant une solution à ce problème, vous devez utiliser jQuery.mandataire

5
répondu Seldaek 2011-01-14 09:44:27

l'explication est que self.onclick ne signifie pas ce que vous pensez qu'il signifie en JavaScript. Il s'agit en fait de la fonction onclick dans le prototype de l'objet self (sans aucune référence à self lui-même).

JavaScript n'a que des fonctions et aucun délégué comme C#, il n'est donc pas possible de passer une méthode et l'objet auquel elle devrait être appliquée en tant que callback.

la seule façon d'appeler une méthode dans un callback est de l'appeler vous-même dans une fonction de rappel. Parce que les fonctions JavaScript sont des fermetures, elles peuvent accéder aux variables déclarées dans le scope dans lequel elles ont été créées.

var obj = ...;
function callback(){ return obj.method() };
something.bind(callback);
2
répondu Vincent Robert 2008-10-11 09:54:00

une bonne explication du problème (j'ai eu des problèmes pour comprendre les solutions décrites jusqu'à présent) est disponible ici .

2
répondu tpk 2009-11-27 17:27:43

j'ai écrit ce plugin...

je pense qu'il sera utile

jquery.rappel

1
répondu 2009-04-16 20:57:54

c'est l'un des points les plus confus DE JS: la variable 'this' signifie l'objet le plus local... mais les fonctions sont aussi des objets, donc 'ceci' pointe là. Il y a d'autres points subtils, mais je ne me souviens pas de tous.

j'évite habituellement d'utiliser 'ceci', il suffit de définir une variable locale' me ' et de l'utiliser à la place.

-4
répondu Javier 2008-10-08 15:10:08