2010-10-26 12 views
8

Je veux faire en Javascript:Se référant à « ce » dans une fermeture parent en javascript

function Z(f) 
{ 
    f(); 
} 

function A() 
{ 
    this.b = function() 
    { 
    Z(function() { this.c() }); 
    } 

    this.c = function() 
    { 
    alert('hello world!'); 
    } 
} 

var foo = new A(); 
foo.b(); 

Il peut être accompli de cette façon:

function Z(f) 
{ 
    f(); 
} 

function A() 
{ 
    var self = this; 
    this.b = function() 
    { 
    Z(function() { self.c() }); 
    } 

    this.c = function() 
    { 
    alert('hello world!'); 
    } 
} 

var foo = new A(); 
foo.b(); 

Y at-il une meilleure façon?

Répondre

6

Garder une référence au parent (comme vous) est une bonne approche, mais pour votre exemple spécifique, il n'y a pas besoin de l'emballage anonyme, vous pouvez passer directement à la fonction, comme ceci:

var self = this; 
this.b = function() 
{ 
    Z(self.c); 
} 

You can test it out here, et sans que cette enveloppe il n'y a en fait pas besoin de la variable self, vous pouvez simplement utiliser this directement, comme ceci:

this.b = function() 
{ 
    Z(this.c); 
} 

You can test that version here.


Comme il semble y avoir une certaine confusion dans les commentaires ci-dessous, le code ci-dessus maintient thispour la question, si vous voulez maintenir le this/contexte dans la fonction de rappel ainsi, utilisez .call()like this:

this.b = function() 
{ 
    Z.call(this, this.c); 
} 

Et pour Z:

function Z(f) 
{ 
    f.call(this); 
} 

You can test it here.

+0

droit, dans cet exemple simple, il n'y a aucune raison pour que l'emballage. Il ne devrait pas être difficile de concevoir un exemple où cela est nécessaire. (Modification des paramètres, etc.) Si vous n'emballez pas, cependant, je suppose que la fermeture est passée comme on pourrait s'y attendre? –

+2

sans le wrapper anonyme, 'c' est appelé avec la mauvaise référence' this'. – Lee

+0

@Jonathan - Oui, correct, dans ce cas, vous voudriez passer une variable comme si vous aviez 'self' qui vous donne accès à ce qui est dans la fermeture. –

1

Il existe un motif souvent appelé "Delegate" qui résout ce problème.

En javascript, une mise en œuvre sans trop de fantaisie pourrait ressembler à ceci:

/** class Delegate **/ 
var Delegate = function(thisRef, funcRef, argsArray) { 
    this.thisRef=thisRef; 
    this.funcRef=funcRef; 
    this.argsArray=argsArray; 
} 
Delegate.prototype.invoke = function() { 
    this.funcRef.apply(this.thisRef, this.argsArray); 
} 
/** static function Delegate.create - convenience function **/ 
Delegate.create = function(thisRef, funcRef, argsArray) { 
    var d = new Delegate(thisRef, funcRef, argsArray); 
    return function() { d.invoke(); } 
} 

Dans votre exemple, vous l'utiliser comme ceci:

this.b = function() { 
    Z(Delegate.create(this, this.c)); 
} 

vous pouvez aussi écrire des fonctions qui s'attendre à recevoir un délégué:

function Z(d) { 
    d.invoke(); 
} 

puis, dans A, votre impl de b devient:

this.b = function() { 
    var d = new Delegate(this, this.c); 

    Z(d); 
    SomeOtherFunc(d); 
} 

Le Delegate fournit juste une façon simple et cohérente d'encapsuler la référence this (que vous avez appelé self), au sein d'une instance d'objet qui peut être traitée comme toute autre instance d'objet. Il est plus lisible et vous évite d'avoir à polluer votre portée de fonction avec des variables superflues comme self. Une mise en œuvre délégué délégué pourrait avoir ses propres méthodes et d'autres états connexes. Il est également possible de créer le délégué de manière à minimiser les problèmes de gestion de la mémoire liés à la portée (bien que le code que j'ai montré ici n'en soit pas un exemple).

+0

Je ne pense pas que cela fonctionne: 'Z (function() {Delegate.create (this, c)});'. Sans le wrapper, il n'y a pas besoin de 'self' et pas de raison pour le Delegate car il est plus court d'écrire' Z (this.c); '. –

+0

@Jonathan - vous avez ajouté un wrapper supplémentaire qui n'est pas dans mon exemple. c'est juste 'Z (Delegate.create (this, c))'; J'ai un peu élargi l'exemple de Delegate pour qu'il soit plus flexible et plus clair. – Lee

+0

@Jonathan - 'Z (this.c)' fonctionnera, aussi longtemps que vous ne vous souciez pas de l'objet 'this' qui se trouve dans' c'. Le code que vous avez montré n'utilise pas 'this' depuis' c', donc il ne présentera aucun problème. Si vous vérifiez (cette légère modification de l'exemple de @ Nick) [http://jsfiddle.net/UQpnX/], vous verrez le problème potentiel, qui est résolu par le wrapper anonyme, dans votre exemple original - ou avec une construction plus formelle comme le délégué. L'un ou l'autre va bien. – Lee

1

Vous pouvez également utiliser

this.b = function() 
{ 
    Z((function() { this.c() }).apply(this)); 
}