web-dev-qa-db-fra.com

Javascript "ce" pointeur dans la fonction imbriquée

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

Supposons 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 sur l'objet "window" global - ce à quoi je ne m'attendais pas. Je ne dois pas comprendre quelque chose correctement parce que 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 il est dans ma première déclaration "si").

Tout pointeur (sans jeu de mots) serait apprécié.

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();
80
JoJoeDad

En JavaScript, l'objet this 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'objet this:

  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'objet this 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'objet window.

111
KylePDavis

this ne 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 transmis en tant que this. Dans le navigateur, l'objet global est identique à window. Par exemple, considérez la fonction suivante,

function someFunction() {
}

et l'objet suivant,

var obj = { someFunction: someFunction };

Si vous appelez la fonction à l'aide d'une syntaxe de méthode telle que,

obj.someFunciton();

alors this est lié à obj.

Si vous appelez someFunction () directement, comme,

someFunction();

alors this est lié à l'objet global, c'est-à-dire window.

Le travail le plus courant consiste à saisir ces informations dans la fermeture, telles que:

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();         
 }      
31
chuckj

Comme cela semble être parmi les questions les plus votées de ce type, 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") { }
    };
    ...    
  }
};
30
user663031

Il y a une différence entre les variables Enclosure et "this". "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'Enclosure. 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 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 "this" être "myObject".

10
Shane

Pour comprendre cette question, essayez d'obtenir la sortie de l'extrait 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 par conséquent, les deux peuvent correctement référencer et accéder à foo.

Dans la fonction interne, cependant, cela ne fait plus référence à myObject. Par conséquent, this.foo n'est pas défini dans la fonction interne, tandis 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 globale; alors que dans ECMA 5, cela dans la fonction interne ne serait pas défini.)

6
ronakshah725

Comme expliqué par Kyle, vous pouvez utiliser call ou apply pour spécifier this dans 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

3
Hari Pachuveetil

vous pouvez également le faire par la méthode .bind (),

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() {

      // now 'this' pointer is referring to the std_obj when calling by bound function
      if (this.activeEffect=="fade") { }
          alert(this.activeEffect);
    }

    var newBoundFunction= doSomeEffects.bind(std_obj);
      newBoundFunction();   
  }
};

nous pouvons en fait définir la valeur de manière explicite avec call (), bind () et apply (). Les trois sont très similaires, mais il est important de comprendre les différences mineures.

Appel et demande sont chacun invoqués immédiatement. L'appel prend n'importe quel nombre de paramètres: celui-ci, suivi des arguments supplémentaires. Appliquer ne prend que deux paramètres: celui-ci, suivi d'un tableau des arguments supplémentaires.

Vous me suivez toujours? Un exemple devrait rendre cela plus clair. Regardez le code ci-dessous. Nous essayons d'ajouter des chiffres. Copiez ceci dans la console de votre navigateur et appelez la fonction.

function add(c, d) {
  console.log(this.a + this.b + c + d);
}
add(3,4);
// logs => NaN

La fonction d'ajout enregistre NaN (pas un nombre). C'est parce que this.a et this.b ne sont pas définis. Ils n'existent pas. Et vous ne pouvez pas ajouter un nombre à quelque chose qui n'est pas défini.

Permet d'introduire un objet dans l'équation. Nous pouvons utiliser call () et apply () pour appeler la fonction avec notre objet:

function add(c, d) {
  console.log(this.a + this.b + c + d);
}
var ten = {a: 1, b: 2};
add.call(ten, 3, 4);
// logs => 10
add.apply(ten, [3,4]);
// logs => 10

Lorsque nous utilisons add.call (), le premier paramètre est celui auquel cela doit être lié. Les paramètres suivants sont passés dans la fonction que nous appelons. Ainsi, dans add (), this.a fait référence à ten.a et this.b fait référence à ten.b et nous obtenons 1 + 2 + 3 + 4 retournés, ou 10.

add.apply () est similaire. Le premier paramètre est ce à quoi cela devrait être lié. Le paramètre suivant est un tableau d'arguments à utiliser dans la fonction.

Et Bind? Les paramètres de bind () sont identiques à call () mais bind () n'est pas appelé immédiatement. Au lieu de cela, bind () renvoie déjà une fonction avec le contexte de cette liaison. Pour cette raison, bind () est utile lorsque nous ne connaissons pas tous nos arguments à l'avance. Encore une fois, un exemple devrait vous aider à comprendre:

var small = {
  a: 1,
  go: function(b,c,d){
    console.log(this.a+b+c+d);
  }
}
var large = {
  a: 100
}

Copiez ce qui précède dans votre console. Appelez ensuite

small.go(2,3,4);
// logs 1+2+3+4 => 10

Cool. Rien de nouveau ici. Mais que se passe-t-il si nous voulons utiliser la valeur de large.a à la place? Nous pouvons utiliser appeler/appliquer:

small.go.call(large,2,3,4);
// logs 100+2+3+4 => 109

Maintenant, que se passe-t-il si nous ne connaissons pas encore les 3 arguments? Nous pouvons utiliser bind:

var bindTest = small.go.bind(large,2);

Si nous consolons notre variable ci-dessus, bindTest, nous pouvons voir avec quoi nous travaillons

console.log(bindTest);
// logs => function (b,c,d){console.log(this.a+b+c+d);}

Rappelez-vous, avec bind, une fonction est retournée qui a déjà cette limite! Donc, notre ceci a été lié avec succès à notre grand objet. Nous avons également déjà passé notre deuxième argument en tant que numéro 2. Plus tard, lorsque nous connaîtrons le reste des arguments, nous pourrons les passer:

bindTest(3,4);
// logs 100+2+3+4 => 109

Pour plus de clarté, voici tout le code ensemble dans un bloc. Regardez-le et copiez-le dans votre console pour vraiment comprendre ce qui se passe!

var small = {
  a: 1,
  go: function(b,c,d){
    console.log(this.a+b+c+d);
  }
}
var large = {
  a: 100
}
small.go(2,3,4);
// logs 1+2+3+4 => 10
var bindTest = small.go.bind(large,2);
console.log(bindTest);
// logs => function (b,c,d){console.log(this.a+b+c+d);}
bindTest(3,4);
// logs 100+2+3+4 => 109

Souvenez-vous de quelques choses:

La valeur de ceci est généralement déterminée par un contexte d'exécution de fonctions.

Dans la portée globale, cela fait référence à l'objet global (l'objet fenêtre).

Lorsque le nouveau mot clé est utilisé (un constructeur), il est lié au nouvel objet en cours de création.

Nous pouvons définir la valeur de ceci explicitement avec call (), bind () et apply ().

Les fonctions fléchées ne le lient pas - au lieu de cela, il est lié lexicalement (c'est-à-dire basé sur le contexte d'origine)

3
Hitesh Kansagara