Pointeur Javascript «this» dans la fonction imbriquée

94

J'ai une question concernant la façon dont le pointeur "this" est traité dans un scénario de fonction imbriquée.

Disons que j'insère cet exemple de code suivant dans une page Web. J'obtiens une erreur lorsque j'appelle la fonction imbriquée "doSomeEffects ()". J'ai vérifié dans Firebug et cela indique que lorsque je suis dans cette fonction imbriquée, le pointeur "this" pointe en fait vers l'objet "window" global - ce à quoi je ne m'attendais pas. Je ne dois pas comprendre quelque chose correctement car je pensais que depuis que j'ai déclaré la fonction imbriquée dans une fonction de l'objet, elle devrait avoir une portée "locale" par rapport à la fonction (c'est-à-dire que le pointeur "this" ferait référence à l'objet lui-même comme comment c'est dans ma première déclaration "si").

Tous les pointeurs (sans jeu de mots) seraient appréciés.

var std_obj = {
  options : { rows: 0, cols: 0 },
  activeEffect : "none",
  displayMe : function() {

    // the 'this' pointer is referring to the std_obj
    if (this.activeEffect=="fade") { }

    var doSomeEffects = function() {

      // the 'this' pointer is referring to the window obj, why?
      if (this.activeEffect=="fade") { }

    }

    doSomeEffects();   
  }
};

std_obj.displayMe();
JoJoeDad
la source
Quelle est exactement votre question?
Sarfraz
2
Lorsqu'il est utilisé dans une fonction, thisfait référence à l'objet sur lequel la fonction est appelée.
Approxiblue
8
Ce que vous pouvez faire dans la portée externe est quelque chose comme var self = this;et ensuite faire référence selfdans la fonction interne via la fermeture.
Kai
1
doSomeEffectsn'est associé à aucun objet en particulier, on thissuppose donc que c'est la fenêtre, la mère de tous les éléments.
approxiblue
3
@JoJoeDad Comment puis-je dire cela diplomatiquement? Mais la réponse donnée par chuckj ci-dessous est de loin la réponse à votre question. Pour vraiment comprendre ce qui se passe, vous devez lire le contexte d'exécution , la chaîne de portée et ce mot clé . Et d'après l'apparence de certaines des réponses ici, d'autres personnes devraient aussi les lire. J'essaye d'évangéliser le bon javascript. C'est pourquoi je prends le temps de donner ces liens.
onefootswill

Réponses:

120

En JavaScript, l' thisobjet est vraiment basé sur la façon dont vous effectuez vos appels de fonction.

En général, il existe trois façons de configurer l' thisobjet:

  1. someThing.someFunction(arg1, arg2, argN)
  2. someFunction.call(someThing, arg1, arg2, argN)
  3. someFunction.apply(someThing, [arg1, arg2, argN])

Dans tous les exemples ci-dessus, l' thisobjet sera someThing. L'appel d'une fonction sans objet parent principal vous donnera généralement l' objet global qui, dans la plupart des navigateurs, signifie l' windowobjet.

KylePDavis
la source
J'ai changé le code en this.doSomeEffects();fonction de votre réponse, mais cela ne fonctionne toujours pas. Pourquoi?
Arashsoft
1
@Arashsoft thisen des this.doSomeEffects()points à std_obj. Comme expliqué dans la réponse ci-dessus, si une fonction n'a pas de référence d'objet, elle thisdoit être un objet de fenêtre.
Shivam
38

Comme cela semble être l'une des questions les plus votées du genre, permettez-moi d'ajouter, après toutes ces années, la solution ES6 utilisant les fonctions fléchées:

var std_obj = {
  ...
  displayMe() {
    ...
    var doSomeEffects = () => {
                        ^^^^^^^    ARROW FUNCTION    
      // In an arrow function, the 'this' pointer is interpreted lexically,
      // so it will refer to the object as desired.
      if (this.activeEffect=="fade") { }
    };
    ...    
  }
};

la source
5
Maintenant c'est du progrès!
Joshua Pinter
Excellente solution simple.
Joshua Schlichting
32

thisne fait pas partie de la portée de fermeture, il peut être considéré comme un paramètre supplémentaire à la fonction qui est liée au site d'appel. Si la méthode n'est pas appelée en tant que méthode, l'objet global est passé en tant que this. Dans le navigateur, l'objet global est identique à window. Par exemple, considérons la fonction suivante,

function someFunction() {
}

et l'objet suivant,

var obj = { someFunction: someFunction };

Si vous appelez la fonction en utilisant une syntaxe de méthode telle que,

obj.someFunciton();

alors thisest lié à obj.

Si vous appelez directement someFunction (), comme,

someFunction();

puis thisest lié à l'objet global, c'est-à-dire window.

Le travail le plus courant consiste à capturer cela dans la fermeture, par exemple,

displayMe : function() {      

    // the 'this' pointer is referring to the std_obj      
    if (this.activeEffect=="fade") { }      
    var that = this;  
    var doSomeEffects = function() {      

      // the 'this' pointer is referring to global
      // that, however, refers to the outscope this
      if (that.activeEffect=="fade") { }      
    }      

    doSomeEffects();         
 }      
chuckj
la source
ça = c'est parfait
Nather Webber
10

Il y a une différence entre les variables de clôture et «ceci». "this" est en fait défini par l'invocateur de la fonction, tandis que les variables explicites restent intactes à l'intérieur du bloc de déclaration de fonction connu sous le nom d'enceinte. Voir l'exemple ci-dessous:

function myFirstObject(){
    var _this = this;
    this.name = "myFirstObject";
    this.getName = function(){
       console.log("_this.name = " + _this.name + " this.name = " + this.name);  
    }
}

function mySecondObject(){
    var _this = this;
    this.name = "mySecondObject";
    var firstObject = new myFirstObject();
    this.getName = firstObject.getName
}

var secondObject = new mySecondObject();
secondObject.getName();

vous pouvez l'essayer ici: http://jsfiddle.net/kSTBy/

Ce qui se passe dans votre fonction est "doSomeEffects ()", est appelé explicitement, cela signifie le contexte ou le "ceci" de la fonction est la fenêtre. si "doSomeEffects" était une méthode prototype, par exemple this.doSomeEffects sur disons "myObject", alors myObject.doSomeEffects () ferait en sorte que "this" soit "myObject".

Shane
la source
4
pour les paresseux et impatients, cette démo enregistre:_this.name = myFirstObject this.name = mySecondObject
ptim
9

Pour comprendre cette question, essayez d'obtenir le résultat de l'extrait de code suivant

var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.log("outer func:  this.foo = " + this.foo);
        console.log("outer func:  self.foo = " + self.foo);
        (function() {
            console.log("inner func:  this.foo = " + this.foo);
            console.log("inner func:  self.foo = " + self.foo);
        }());
    }
};
myObject.func();

Le code ci-dessus affichera les éléments suivants sur la console:

outer func:  this.foo = bar
outer func:  self.foo = bar
inner func:  this.foo = undefined
inner func:  self.foo = bar

Dans la fonction externe, this et self font référence à myObject et peuvent donc correctement référencer et accéder à foo.

Dans la fonction interne, cependant, cela ne fait plus référence à myObject. En conséquence, this.foo n'est pas défini dans la fonction interne, alors que la référence à la variable locale self reste dans la portée et y est accessible. (Avant ECMA 5, cela dans la fonction interne ferait référence à l'objet de fenêtre global; alors que, à partir de ECMA 5, cela dans la fonction interne serait indéfini.)

ronakshah725
la source
1
Dans la fonction interne, thisfait référence à l'objet window (dans un environnement de navigateur) ou à l'objet GLOBAL (dans un environnement node.js)
raneshu
2
Pourquoi cela arrive-t-il?
setu
@raneshu "use strict" et thisne fait plus référence à la fenêtre
rofrol
3

Comme expliqué par Kyle, vous pouvez utiliser callou applypour spécifier thisdans la fonction:

Voici ce concept appliqué à votre code:

var std_obj = {
    options: {
        rows: 0,
        cols: 0
    },
    activeEffect: "none",
    displayMe: function() {

        // the 'this' pointer is referring to the std_obj
        if (this.activeEffect == "fade") {}

        var doSomeEffects = function() {
            // the 'this' pointer is referring to the window obj, why?
            if (this.activeEffect == "fade") {}
        }

        doSomeEffects.apply(this,[]);
    }
};

std_obj.displayMe();

JsFiddle

Hari Pachuveetil
la source
0

J'ai également reçu un avertissement " Accès de référence potentiellement invalide à un champ de classe via ce "

class MyListItem {
    constructor(labelPrm) {
        this._flagActive = false;
        this._myLabel = labelPrm;
        labelPrm.addEventListener('click', ()=>{ this.onDropdownListsElementClick();}, false);
    }

    get myLabel() {
        return this._myLabel
    }
    get flagActive(){
        return this._flagActive;
    }

    onDropdownListsElementClick(){console.log("Now'this'refers to the MyListItem itself")}}//end of the class
CodeToLife
la source
0

Comme cela n'a pas été mentionné, je mentionnerai que l'utilisation .bind()est une solution -


        doSomeEffects=doSomeEffect.bind(this);
        doSomeEffects();   
      }
    };

    std_obj.displayMe();

Voici un exemple plus simple -

bad = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x() 
  } 
};
bad.z() // prints 'undefined'

good = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x=x.bind(this);
    x();
  } 
};
good.z() // prints 'NAME'

Il est vrai que l'utilisation d'une fonction de flèche =>semble plus simple et facile pour le programmeur. Cependant, il faut garder à l'esprit qu'une portée lexicale est susceptible de nécessiter plus de travail en termes de traitement et de mémoire pour configurer et maintenir cette portée lexicale, par rapport à la simple association d'une fonction thisà un pointeur via .bind().

Une partie de l'avantage de développer des classes dans JS était de fournir une méthode pour rendre la thisprésence et la disponibilité plus fiables, pour s'éloigner de la programmation fonctionnelle et des portées lexicales, et ainsi réduire les frais généraux.

De MDN

Considérations relatives aux performances Il n'est pas judicieux de créer inutilement des fonctions dans d'autres fonctions si des fermetures ne sont pas nécessaires pour une tâche particulière, car cela affectera négativement les performances du script en termes de vitesse de traitement et de consommation de mémoire.

Craig Hicks
la source