2010-10-25 21 views
3

Je tente de faire une classe en javascript. Je le crée avec l'objet type JSON.JavaScript Classe + Propriétés ne pas coller à leurs valeurs

Faire ceci:

Foo = { 
    PubId: '', 

    Init:function(oCallback) 
    { 
     this.sendCommand('INIT', {}, oCallback); 
    }, 

    sendCommand : function(sCommand, aParams, oCallback) 
    { 

     setTimeout(oCallback, 1000, '{"response":"INIT","time":1287982024,"pubid":"4cc50bc47c7b3"}'); 

     return true; 
    }, 
    onData : function(sData) 
    { 
     var aRes = JSON.parse(sData); 

     this.PubId = aRes.pubid; 
     alert(this.PubId); 
     return this.PubId; 
    }, 
    umtest:function(){ alert(this.PubId); } 
} 

Je puis aussi le faire après y compris le script:

Foo.Init(Foo.onData); 

Le problème est que le this.PubId est mis à jour dans la méthode onData, mais en dehors de celui-ci , le pubide est vide. Je suis un peu nouveau dans les classes javascript, donc je ne suis pas sûr de ce qui doit être fait alors j'espérais que quelqu'un pourrait m'aider à sortir. :)

Merci pour votre temps!

Répondre

2

Eh bien, vous avez deux problèmes ici. Le premier problème ne comprend pas comment this fonctionne en Javascript. Lorsque Foo.onData est appelé via setTimeout(oCallback, ...) le this se référera à l'objet global pas Foo.

Pour appeler avec Foo comme this vous devez changer votre code:

sendCommand: function (sCommand, aParams, oCallback) { 
    var that = this;   // save this reference 
    setTimeout(function() { 
     oCallback.call(that, // call the function with it 
       '{"response":"INIT","time":1287982024,"pubid":"4cc50bc47c7b3"}'); 
    }, 1000); 
    return true; 
}, 

Afin de tester ce qui est changé de place ce code onData:

// is `this` really Foo or the global object? 
alert(this === Foo); // should be true 
alert(this === window); // should be false 

Dans la version mise à jour this référencera correctement Foo en tant qu'objet d'invocation.

Le deuxième problème vous pourriez être confronté avec est que votre fonction appelée avec setTimeout ne sera exécuté après 1000 ms = 1s, donc si vous vérifiez simplement alert(Foo.PubId) en dehors de Foo vous obtiendrez une chaîne vide (parce que le rappel hasn pas encore appelé).

Afin de tester si Foo.PubId est en effet changé:

// execute the check after 2s so as to 
// make sure the callback has been called 
setTimeout(function() { 
    alert(Foo.PubId); 
}, 2000); 

Vous pouvez vérifier le cas de test complet here.

+1

Merci pour l'aide! Et même pour tout le monde. Je voudrais pouvoir voter pour vous tous, mais je suppose que je dois être un utilisateur du site un peu plus longtemps avant de pouvoir le faire. Quoi qu'il en soit, merci pour la solution galambalazs. Je n'avais aucune idée que Javascript passait la fonction au lieu de la méthode. Boiteux! Anywho, avec le code. (Ce site jsbin est trop cool!) – dab

+0

Amusez-vous avec le codage! :) – galambalazs

0

L'onData est exécuté séparément de la classe.

Vous devez appeler comme ça

Foo.Init(function(d) { Foo.onData(d);}); 

pour que cela fonctionne comme excpected.

+0

Ceci est une * fonction anonyme *, pas une * fermeture *. – galambalazs

0

Il s'agit d'un problème d'étendue. Lorsque vous passez Foo.onData à votre fonction init, il "perd" la connexion à la classe. Les fonctions sont des objets de première classe, de sorte qu'ils peuvent exister par eux-mêmes. Vous pouvez effectuer les opérations suivantes:

Foo = { 
    PubId: '', 

    Init:function(oCallback, scope) 
    { 
     this.sendCommand('INIT', {}, oCallback, scope); 
    }, 

    sendCommand : function(sCommand, aParams, oCallback, scope) 
    { 
     var cb = oCallback; 
     if(scope) { 
      cb = function(data) { 
       oCallback.call(scope, data); 
      }; 
     } 

     setTimeout(cb, 1000, '{"response":"INIT","time":1287982024,"pubid":"4cc50bc47c7b3"}'); 

     return true; 
    } 
    //... 
} 

puis appelez avec

Foo.Init(Foo.onData, Foo); 

Le premier paramètre qui est passé à call() deviendra this dans la méthode.

+0

Aha, merci pour la suggestion! : D – dab

1

Lorsqu'une méthode appartenant à un objet est appelée à l'aide de la notation par points, son contexte (où this pointe vers) est l'objet auquel elle est connectée. Par exemple:

// Context: `this` is `myObj` 
myObj.myMethod(); 

Cependant, lorsque vous stockez une référence à cette fonction dans une autre variable, il perd cette relation contextuelle et le contexte devient l'objet global:

// Context: `this` is the global object - `window` in a web browser 
var myMethod = myObj.myMethod; 
myMethod(); 

Lors de l'exécution d'une méthode, vous pouvez utiliser la méthode call ou apply de préciser le contexte que vous souhaitez à exécuter dans:

// Context: `this` is `myObj` 
var myMethod = myObj.myMethod; 
myMethod.call(myObj); 

dans votre exemple particulier , Je voudrais envisager d'utiliser une nouvelle méthode introduite dans les versions plus récentes de JavaScript nommé bind. bind est pris en charge par IE9, Firefox 4 et les versions plus récentes de Google Chrome et Safari, et il vous permet de "verrouiller" une fonction à un contexte particulier. Vous pouvez mettre en œuvre la plupart de ses fonctionnalités dans les navigateurs qui ne prennent pas en charge à l'aide du code ci-dessous (extrait de la documentation MDC pour Function.prototype.bind):

// PARTIAL WORKAROUND for Function.prototype.bind 
if (!Function.prototype.bind) 
    Function.prototype.bind = function(context /*, arg1, arg2... */) { 
     'use strict'; 
     if (typeof this !== 'function') throw new TypeError(); 
     var _slice = Array.prototype.slice, 
      _concat = Array.prototype.concat, 
      _arguments = _slice.call(arguments, 1), 
      _this = this, 
      _function = function() { 
       return _this.apply(this instanceof _dummy ? this : context, 
        _concat.call(_arguments, _slice.call(arguments, 0))); 
      }, 
      _dummy = function() {}; 
     _dummy.prototype = _this.prototype; 
     _function.prototype = new _dummy(); 
     return _function; 
}; 

l'utiliser dans votre code est très simple:

Foo.Init(Foo.onData.bind(Foo)); 
+0

Merci pour la suggestion! Je préfère ne pas compter sur une solution de contournement cependant pour l'obtenir pour fonctionner avec les navigateurs plus anciens. :/ – dab

+0

@dab: ce n'est pas vraiment une "solution de contournement", c'est juste une implémentation moins fonctionnelle. Cette fonctionnalité manquante n'est pas requise ici. Pour ce que vous devez faire, * bind() * aurait été la meilleure solution car vous maîtrisez le contexte depuis le point de référence (où vous appelez la méthode Init). Et pour les nouveaux navigateurs, le résultat aurait été encore plus rapide car il existe une solution native. –