2010-08-31 15 views
2

J'ai deux Javascript « objets » semblables à so ....Avoir la valeur correcte de « ceci » dans JS

var Object2 = new (function() { 
    this.FetchData = function(callback) { 
     // do some stuff 
     callback(data); 
    }; 
}); 

var Object1 = new (function() { 

    this.DisplayStuff = function() { 

    }; 

    this.LoadData = function() { 
     Object2.FetchData(this.OnData); 
    }; 

    this.OnData = function(data) { 
     // this == window 
     this.DisplayStuff(); // doesn't work 
    }; 

}); 

Lorsque Object1 reçoit le rappel à OnData, la valeur de « ce » est réglé à la fenêtre. Est-il possible de contourner ce problème afin que la valeur de "this" à l'intérieur de OnData soit l'instance de Object1 au lieu de window?

+0

Qui appelle Object1.OnData? L'avez-vous attaché à un événement? Pourriez-vous montrer le code de la façon dont vous l'avez attaché à un événement? –

+0

Où l'objet 2 appelle-t-il l'objet 1? Le rappel est-il l'une des méthodes de l'objet 2? –

Répondre

3

La façon simple de le faire est de stocker une référence à cette dans une variable, puis en utilisant la méthode call():

this.LoadData = function() { 
    var self = this; 
    Object2.FetchData(function() { self.OnData.call(self) }); 
}; 

Si vous faites cela beaucoup, vous voudrez peut-être envisager d'utiliser la méthode .bind() sur le prototype de fonction dans ECMAScript 5ème édition. Cette méthode peut être mise en œuvre lorsque non pris en charge:

// From Prototype.js 
if (!Function.prototype.bind) { // check if native implementation available 
    Function.prototype.bind = function(){ 
    var fn = this, args = Array.prototype.slice.call(arguments), 
     object = args.shift(); 
    return function(){ 
     return fn.apply(object, 
     args.concat(Array.prototype.slice.call(arguments))); 
    }; 
    }; 
} 

Et l'appel de fonction résultante:

this.LoadData = function() { 
    Object2.FetchData(this.OnData.bind(this)); 
}; 

PrototypeJS - bind()

+0

Slick! Quelque chose comme ça est ce que j'espérais. –

0

La solution de base consiste à forcer le contexte sur le rappel avec apply. (Ou call La différence réside dans la façon dont les paramètres sont transmis: soit un tableau, soit «normalement») Voir la documentation MDC.

0

Pour un rappel, vous êtes coincé avec 'ceci' étant la fenêtre (généralement). Cependant, vous pouvez ajouter un membre à Object1 pointant sur lui-même (un grand nombre de fois il obtient appelez 'self', c'est-à-dire "var self = this;"), puis utilisez-le, c'est-à-dire self.DisplayStuff().

5

Une technique couramment utilisée dans les cadres est de permettre à l'appelant de décider de ce que le this contexte devrait être pour la fonction de rappel. Ainsi, la fonction FetchData ressemble (grâce à @Andy pour me dire au sujet de la portée par défaut lorsque le contexte est nul ou non défini - il est l'objet global),

this.FetchData = function(callback, context) { 
    callback.call(context, data); 
}; 

Lorsque vous appelez FetchData, passer Object1 que le contexte de la rappel,

Object2.FetchData(this.OnData, this); 

Une autre option consiste à se lier à la fonction OnData avec Object1 utilisant Function.prototype.bind

Object2.FetchData(this.onData.bind(this)); 
+0

J'aimerais pouvoir choisir deux réponses. C'est une bonne réponse. –

+0

Pas besoin de 'context || window', si context est * undefined * ou * null * il sera déjà par défaut à l'objet global, qui est * window * dans le cas des pages web. –

+1

@Andy - merci de pointer notre comportement par défaut pour un contexte non spécifié. merci @T. Stone - content que vous l'ayez aimé – Anurag

0

L'ambiguïté de "ceci" est vraiment ennuyante avec javascript. Donc j'évite juste "ceci" autant que possible. Je le fais de cette façon:

var Object2 = (function() { // no need for a "new" keyword any more 

    var self = {}; // instead of the auto-supplied "this", create my own "self" 

    self.FetchData = function(callback) { 
     // do some stuff 
     callback(data); 
    }; 
    return self; // don't forget to return "self" 
}); 

var Object1 = (function() { 

    var self = {}; // instead of the auto-supplied "this", create my own "self" 

    self.DisplayStuff = function() { 

    }; 

    self.LoadData = function() { 
     Object2.FetchData(this.OnData); 
    }; 

    self.OnData = function(data) { 
     self.DisplayStuff(); 
    }; 

    return self; // don't forget to return "self" 

}); 

Vous perdez la possibilité d'utiliser Object.prototype avec cette technique, vous perdez la possibilité de tester en utilisant « instanceof », mais vous ne jamais avoir à penser à ce que « cela » est.

+0

Pourquoi ne pas se contenter de cela? Alors vous ne perdez pas ces capacités. –

+0

Cela fonctionne très bien aussi. Je pense que je commence à le faire de cette façon parce que je me sentais en colère contre "ceci" et était excité à l'idée de le bannir. – morgancodes

+0

En fait, ce n'est pas vrai. J'ai expérimenté l'approche de "l'héritage parasitaire" de Crockford, qui évite "ceci". http://blog.higher-order.net/2008/02/21/javascript-parasitic-inheritance-power-constructors-and-instanceof/ – morgancodes