2010-10-18 18 views
2

Modifier: Je dois m'excuser, le problème que j'ai posté n'existe pas réellement dans le code que j'ai posté, parce que je l'ai trop simplifié. Je vais essayer de poster quelque chose plus tard.Comment l'opérateur Javascript "this" gère-t-il les étendues?

La suppression serait également acceptable, mais il y a trop de réponses pour le moment, donc je ne peux pas le faire moi-même.

Edit2: Ok, va ici:

Soit,

function F() { 

    this.field = "value" ; 

    var init = function(value) { 
     this.field = value ; 
    } ; 

    this.method = function() { 
     return this.field ; 
    } ; 

    init(arguments[0]) ; 

} 

Maintenant, instanciation de type F,

var f = new F("newValue") ; 

règlera la valeur à l'objet Window comme this pointe vers lui lorsqu'il est appelé depuis la fermeture.

Reliure this au Function,

function F() { 

    var self = this ; 

    this.field = "value" ; 

    var init = function(value) { 
     self.field = value ; 
    } ; 

    this.method = function() { 
     return this.field ; 
    } ; 

    init(arguments[0]) ; 

} 

va résoudre le problème.

Pourtant, quelle est la raison de ce comportement - imho - impair?

+2

Je suis un peu surpris que cela fonctionne du tout. Vos fonctions sont assignées aux variables "var", qui devraient disparaître dès le retour de la fonction. Ils ne sont jamais attachés à l'objet lui-même, donc 'f.method' n'aurait aucune valeur (donc par définition, il ne serait pas appelable). – cHao

+0

Cela pourrait aider à l'expliquer: http://jibbering.com/faq/notes/closures/ –

+1

@Brian: Qui était-ce? Cela pourrait aider à répondre à la question, mais n'expliquerait pas pourquoi 'f.method()' n'échoue pas avec des erreurs sur les valeurs non définies. – cHao

Répondre

1

Comme il se trouve, c'est l'un des coins sombres de la spécification ECMA pour ECMAScript (JavaScript par conséquent):

Enfin, une valeur est attribuée pour une utilisation avec le ce mot clé. Si la valeur affectée fait référence à un objet, les accesseurs de propriété sont préfixés avec les propriétés de référence de ce mot clé. Si la valeur affectée (en interne) est nulle, le mot-clé this fera référence à l'objet global.

(http://jibbering.com/faq/notes/closures/, grâce à @ Brian Flanagan pour le lien)

Apparemment, pendant l'exécution d'une fonction qui est affectée à une variable (à moins de un autre Function) le contexte est perdu, la portée est donc définie sur null et this fera référence à WindowObject.


Je ne suis pas sûr que ce soit quelque chose à attendre d'un Function initialisé comme une variable locale. En règle générale, je m'attendrais à ce qu'une fermeture ait accès à la portée qu'elle a été créée alors qu'elle est encore en mémoire, y compris son prédécesseur dans la chaîne de portée.

Ce comportement est différent d'une fonction qui est la propriété d'un FunctionObject, où le contexte ne soit pas perdu et this pointera correctement au propriétaire du membre Function (qui est le FunctionObject ou un membre en fonction sur combien de couches il y a).


La solution à ce problème (tout en maintenant Function privés membres, soit les membres qui ne sont accessibles de l'intérieur de la portée de la Function) est enveloppant la fonction dans un autre Function utilisant Function.prototype.apply (ou Function.prototype.call) pour définir le contexte manuellement:

function F() { 

    this.field = "value" ; 

    var self = this ; 

    var init = function(value) { 

     (function() { 
      this.field = value ; 
     }).apply(self,value) ; 

    } ; 

    this.method = function() { 
     return this.field ; 
    } ; 

    init(arguments[0]) ; 

} 

Ceci est similaire à Function.prototype.bind introduit en JavaScript 1,85, ainsi que la bibliothèque prototypejs.

Editer: Les parenthèses ont été omises autour de la fonction ci-jointe dans init. Cela se traduira par un SyntaxError.

1

Il n'est pas correct d'appeler this un "opérateur". C'est une référence d'objet établie par l'exécution lors de l'activation de la fonction.

Javascript est juste une langue différente qui fait les choses de différentes manières. Que this est sous le contrôle du programmeur est merveilleusement puissant.

Ce code ne fonctionne pas non plus, à moins que vous ne spécifiiez autre chose qu'un objet prototype pour "F". Ni "method" ni "method2" ne sont appelables à partir d'une référence à une instance de "F".

+1

Le MDC liste 'this' sous les opérateurs. De plus, vous n'offrez pas vraiment d'explication. – FK82

+0

Apparemment le MDC et moi différons sur la définition de "opérateur" :-) – Pointy

+0

Il est en effet listé sous "Opérateurs" dans la référence MDC, mais la norme l'appelle juste un "mot-clé", ce que je pense est beaucoup plus précis. – Pointy

2

Ceci est une référence à l'objet sur lequel une fonction a été appelée. Donc, dans votre appel f.method(), this est "f". PAS "F".

Le fait que var self = this; œuvres ne sont pas à cause de cette déclaration elle-même, mais en raison du fait que method2() est un closure

Pour être plus précis, la self variable à l'intérieur method2() a une portée déterminée par F() - en d'autres termes, "self" à l'intérieur method2() se réfèrera TOUJOURS à la valeur de self qui existait lorsque "F" était défini (ce qui, à l'époque, était bien sûr "F" car à ce moment le contexte d'objet courant était "F") .

1

Pour la définition de la méthode, vous voulez sans doute faire quelque chose comme:

this.method = function() 
{ 
    return this.field; 
} 
1

Est-ce l'exemple que vous recherchez?

var F = function() { 

    var self = this ; //!! points correctly to the Function Object 

    this.field = "someValue" ; 

    var helper1 = function(){ 
     return self.field; 
    } 
    var helper2 = function(){ 
     return this.field; 
    } 
    this.method = function(){ 
     return helper1(); 
    } 
    this.method2 = function(){ 
     return helper2(); 
    } 
} 

var f = new F() ; 
    console.log(f.method()) ; 
    console.log(f.method2()) ; 

Dans le cas de la méthode, il appelle aide qui utilise soi-même, qui pointe vers f quand il est créé et donc cela fonctionne.

Dans le cas de la méthode 2, elle utilise helper2, qui l'utilise. Mais la signification de ceci à l'intérieur de helper2 est la portée 'globale' et renvoie donc undefined.

+0

Correct, un oubli de ma part. Je l'ai corrigé. – FK82