3 votes

La valeur de "this" en javascript change, mais je ne comprends pas pourquoi.

Je suis totalement novice en Javascript et j'essaie de me faire une idée d'OLN. Ce que je rencontre, c'est que, lorsque j'appelle une méthode objet à partir d'une autre méthode sur le même objet, la valeur de la valeur locale de 'this' dans la méthode appelée change. Voici mon code :

var generator = {
    generateForLevelSkillAndCount : function(level, skill, count) {
        var functionCall = this['generate_' + level + '_' + skill];
        return functionCall(count);
    },
    generate_0_4 : function(count) {
        return this.generate_generic_dots(count, 3);
    },
    generate_generic_dots : function(count, maxDots) {
        /* do cool stuff and return it */
    }
};

Donc, j'appelle generator.generateForLevelSkillAndCount(0, 4, 20) et cela fonctionne correctement, en appelant generate_0_4(count) . Cependant, c'est là qu'il échoue, la console Javascript de Chrome m'indiquant "Uncaught TypeError : Object [object DOMWindow] has no method 'generate_generic_dots'".

J'en sais assez pour savoir que le problème est que la valeur de this en generate_0_4 est un objet DOMWindow, plutôt qu'un générateur (ce qui est le cas de l'objet this pointe vers dans generateForSkillLevelAndCount mais je n'arrive pas à comprendre pourquoi ça pourrait arriver.

Mise à jour : J'ai mis à jour le code d'exemple selon la suggestion de CMS pour se débarrasser de eval mais la même erreur est renvoyée, ce qui signifie qu'il ne s'agit pas seulement d'un problème de sécurité. eval bogue.

8voto

Ben Blank Points 21786

En JavaScript, l'objet contexte ( this ) est réglé sur l'"objet global" ( window dans les navigateurs), sauf si la méthode est accessible en tant que propriété d'un objet. Par conséquent :

var foo = { bar: function() { alert(this.baz); }, baz: 5 };
var bar = foo.bar;
var baz = 3;

foo.bar();    // alerts 5, from foo
foo["bar"](); // alerts 5, from foo
bar();        // alerts 3, from the global object

Notez que les trois appels de fonction sont destinés à la fonction exactement la même fonction !

Donc, dans votre code, vous assignez la méthode souhaitée à functionCall et de l'appeler directement, ce qui fait que la fonction utilise window comme objet de contexte. Il y a deux façons de contourner ce problème : accéder à la méthode en tant que propriété d'un objet ou utiliser la fonction .call() o .apply() :

function generateForLevelSkillAndCount1(level, skill, count) {
    return this['generate_' + level + '_' + skill](count);
}

function generateForLevelSkillAndCount2(level, skill, count) {
    var functionCall = this['generate_' + level + '_' + skill];
    return functionCall.call(this, count);
}

3voto

CMS Points 315406

Tout d'abord, je vous encourage à éviter l'eval là où vous n'en avez pas besoin, par exemple, dans votre première fonction :

//...
generateForLevelSkillAndCount : function(level, skill, count) {
    var functionCall = this['generate_' + level + '_' + skill];
    return functionCall(count);
},
//...

Vous pouvez utiliser l'accesseur de propriété entre crochets à la place. eval c'est inutile dans ce cas.

Maintenant, je suppose que vous essayez votre code sur la console de Chrome, et eval échoue parce que la console a un bug, lorsque eval est invoquée à partir d'une FunctionExpression (telle que generateForLevelSkillAndCount ), le code évalué utilise le contexte global pour son environnement de variables et son environnement lexical.

Voir cette réponse pour plus d'informations sur ce bogue.

Modifier : Après avoir relu votre code, le problème se produit parce que vous perdez la référence de l'objet de base lorsque vous assignez la fonction à votre functionCall variable, vous pouvez soit :

Invoquer la fonction directement, sans utiliser cette variable :

//...
generateForLevelSkillAndCount : function(level, skill, count) {
    this['generate_' + level + '_' + skill](count);
},
//...

Ou encore utiliser votre variable, mais persistent les this valeur :

//...
generateForLevelSkillAndCount : function(level, skill, count) {
    var functionCall = this['generate_' + level + '_' + skill];
    return functionCall.call(this, count);
},
//...

Plus d'informations sur this ...

1voto

Dave Ward Points 36006

Vous pouvez contrôler le contexte d'exécution de l'appel de la méthode en utilisant la fonction appeler() :

var generator = {
  generateForLevelSkillAndCount : function(level, skill, count) {
    return this['generate_' + level + '_' + skill].call(this, count);
  },
  generate_0_4 : function(count) {
    return this.generate_generic_dots.call(this, count, 3);
  },
  generate_generic_dots : function(count, maxDots) {
    /* do cool stuff and return it */
  }
};

0voto

Je suis aussi déconcerté que toi, mais as-tu pensé à vérifier ce qui se passe si tu appelles generate_0_4 explicitement, au lieu de l'analyser à travers eval() ?

0voto

cbednarski Points 3579

Lorsque vous appelez generate_0_4 de manière dynamique (en utilisant une to_string() ), il est renvoyé à generateForLevelSkillAndCount comme une fonction ad-hoc. Parce que c'est dans Window plutôt que Object il ne peut pas faire référence à this et l'appel interne échoue parce que this n'existe pas dans ce contexte.

Voici comment voir ce qui se passe :

generate_0_4 : function(count) {
    throw(this);
    return this.generate_generic_dots(count, 3);
},

Avec generator.generateForLevelSkillAndCount(0, 4, 1); vous obtenez [object Window] o [object DOMWindow] .

Avec generator.generate_0_4(1); vous obtenez ce que vous attendez (et ce qui fonctionne) : [object Object] o #<an Object> .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X