Que signifie 'var that = this;' dire en JavaScript?

351

Dans un fichier JavaScript, j'ai vu:

function Somefunction(){
   var that = this; 
   ... 
}

Quel est le but de la déclarer thatet de lui attribuer thiscela?

Chris
la source
3
doublon possible de var self = ceci?
Bergi
6
Le hack "ceci" et "que" n'est pas requis pour les fonctions fléchées. Avec les fonctions fléchées "cela" fonctionne comme prévu. Voir ici pour plus de détails ES6 en profondeur: fonctions fléchées
satguru srivastava
1
ici le concept de ceci est expliqué scotch.io/@alZami/understanding-this-in-javascript
AL-zami
Une grande explication sur ce comportement mystérieux basé sur le contexte ici
RBT
Les explications les plus récentes et mises à jour peuvent être trouvées ici
Sukrit Gupta

Réponses:

486

Je vais commencer cette réponse par une illustration:

var colours = ['red', 'green', 'blue'];
document.getElementById('element').addEventListener('click', function() {
    // this is a reference to the element clicked on

    var that = this;

    colours.forEach(function() {
        // this is undefined
        // that is a reference to the element clicked on
    });
});

Ma réponse l'a démontré à l'origine avec jQuery, qui n'est que très légèrement différent:

$('#element').click(function(){
    // this is a reference to the element clicked on

    var that = this;

    $('.elements').each(function(){
        // this is a reference to the current element in the loop
        // that is still a reference to the element clicked on
    });
});

Parce que thischange fréquemment lorsque vous modifiez la portée en appelant une nouvelle fonction, vous ne pouvez pas accéder à la valeur d'origine en l'utilisant. thatSi vous le remplacez par, vous pouvez toujours accéder à la valeur d'origine de this.

Personnellement, je n'aime pas l'utilisation de thatl'alias. Il est rarement évident de savoir à quoi il se réfère, surtout si les fonctions sont plus longues que quelques lignes. J'utilise toujours un alias plus descriptif. Dans mes exemples ci-dessus, j'utiliserais probablement clickedEl.

solitaire
la source
149
Je vais habituellement avec var self = this;. Le mot thatsemble impliquer que la variable est n'importe quoi MAIS this.
David Murdoch
13
@ David Oui, j'ai pensé que c'était quelque peu trompeur. Mais si, comme le dit Crockford, c'est une convention, est-il sage de suivre cette voie. Je suis tout à fait d'accord avec vous, c'est beaucoup plus logique.
El Ronnoco
4
@El Ronnoco, mais "Il a les cheveux gris et une barbe grasse et sa manière fait penser à un vieil homme grincheux qui hurle aux enfants de descendre de sa pelouse." - blogging.compendiumblog.com/blog/software-for-humans/0/0/… ;-p
David Murdoch
7
@ElRonnoco: C'est un appel à l'autorité, cependant. Si nous ne faisons que ce que "les gens célèbres" disent que nous devrions faire, nous nous dirigeons vers le désastre.
Courses de légèreté en orbite le
3
La forEachfonction prend un deuxième argument optionnel qui est la liaison de la fonction. colours.forEach(function(){/* 'this' is bound correctly --> */}, this);Il faut donc ajouter une note qui var that = thisn'est pas réellement nécessaire avec forEach.
Matt Clarkson
107

Depuis Crockford

Par convention, nous faisons un privé que variable. Ceci est utilisé pour mettre l'objet à la disposition des méthodes privées. Il s'agit d'une solution de contournement pour une erreur dans la spécification de langage ECMAScript qui provoque ce paramètre n'est pas défini correctement pour les fonctions internes.

JS Fiddle

function usesThis(name) {
    this.myName = name;

    function returnMe() {
        return this;        //scope is lost because of the inner function
    }

    return {
        returnMe : returnMe
    }
}

function usesThat(name) {
    var that = this;
    this.myName = name;

    function returnMe() {
        return that;            //scope is baked in with 'that' to the "class"
    }

    return {
        returnMe : returnMe
    }
}

var usesthat = new usesThat('Dave');
var usesthis = new usesThis('John');
alert("UsesThat thinks it's called " + usesthat.returnMe().myName + '\r\n' +
      "UsesThis thinks it's called " + usesthis.returnMe().myName);

Cette alerte ...

Qui pense que ça s'appelle Dave

Usages Ceci pense que cela s'appelle undefined

El Ronnoco
la source
2
Merci, ça résume assez bien pour moi.
Chris
3
J'ai lu ça, je n'ai pas compris car il n'y avait pas de détail, j'ai cherché sur Google, trouvé cette page. Là où je suis à nouveau pointé vers la même phrase. D'où le downvote.
Aditya MP
3
C'est un bon point, je dirais que quelqu'un qui ne connaît pas JavaScript aurait du mal à saisir le concept de ma seule réponse. J'ai répondu très brièvement (et j'ai fait un lien vers la page pour laquelle vous êtes allé sur Google ..) Je dirais que la réponse de lonesomeday est la plus claire, bien que je l'aurais quand même préférée en JS simple par opposition à un exemple jQuery.
El Ronnoco
16
Je ne m'offusque pas. C'est agréable de voir quelqu'un qui commente lors du vote en aval!
El Ronnoco
4
Le problème avec la réponse de Crockford est que la thatvariable n'est pas du tout utilisée dans son exemple. Cela donne l'impression que la simple création d'une variable holding thisfait quelque chose pour le reste du code.
r3m0t le
86

Il s'agit d'un hack pour faire fonctionner les fonctions internes (fonctions définies à l'intérieur d'autres fonctions) comme elles le devraient. En javascript, lorsque vous définissez une fonction à l'intérieur d'une autre, elle est thisautomatiquement définie sur la portée globale. Cela peut être déroutant car vous vous attendez thisà avoir la même valeur que dans la fonction externe.

var car = {};
car.starter = {};

car.start = function(){
    var that = this;

    // you can access car.starter inside this method with 'this'
    this.starter.active = false;

    var activateStarter = function(){
        // 'this' now points to the global scope
        // 'this.starter' is undefined, so we use 'that' instead.
        that.starter.active = true;

        // you could also use car.starter, but using 'that' gives
        // us more consistency and flexibility
    };

    activateStarter();

};

C'est spécifiquement un problème lorsque vous créez une fonction comme méthode d'un objet (comme car.startdans l'exemple) puis créez une fonction à l'intérieur de cette méthode (comme activateStarter). Dans la méthode de niveau supérieur thispointe vers l'objet, c'est une méthode de (dans ce cas, car) mais dans la fonction interne thispointe maintenant vers la portée globale. C'est une douleur.

La création d'une variable à utiliser par convention dans les deux étendues est une solution à ce problème très général avec javascript (bien qu'il soit également utile dans les fonctions jquery). C'est pourquoi le nom à consonance très générale thatest utilisé. C'est une convention facilement reconnaissable pour surmonter une lacune dans la langue.

Comme El Ronnoco fait allusion à Douglas Crockford pense que c'est une bonne idée.

Waylon Flinn
la source
8
Je suppose que c'est une réponse plus utile que celle acceptée. Parce que cela clarifie la raison pour laquelle Crockford a inventé "ça" alors que la réponse à propos de jQuery ne le fait pas.
Konstantin Smolyanin
4
C'est en fait un meilleur exemple que la réponse acceptée. Il explique à quoi cela ressemble comme "une erreur dans la spécification du langage ECMAScript qui provoque un mauvais réglage des fonctions internes", a déclaré Douglas.
kakacii
1
Vous voudrez peut-être faire en sorte que la grammaire soit correcte. Je sais que cela ressemble plus à une faute de frappe, mais cela pourrait confondre les débutants en javascript car cette question est plutôt de type débutant. Je veux dire que cela devrait être: var car = {}; car.starter = {}; car.start = function () {...}
kakacii
1
@kakacii Merci. Fixé maintenant.
Waylon Flinn
9

L'utilisation de thatn'est pas vraiment nécessaire si vous effectuez une solution de contournement en utilisant call()ou apply():

var car = {};
car.starter = {};

car.start = function(){
    this.starter.active = false;

    var activateStarter = function(){
        // 'this' now points to our main object
        this.starter.active = true;
    };

    activateStarter.apply(this);
};
Adela
la source
3

Parfois, thispeut faire référence à une autre portée et faire référence à autre chose, par exemple, supposons que vous vouliez appeler une méthode constructeur à l'intérieur d'un événement DOM, dans ce cas thisfera référence à l'élément DOM et non à l'objet créé.

HTML

<button id="button">Alert Name</button>

JS

var Person = function(name) {
  this.name = name;
  var that = this;
  this.sayHi = function() {
    alert(that.name);
  };
};

var ahmad = new Person('Ahmad');
var element = document.getElementById('button');
element.addEventListener('click', ahmad.sayHi); // => Ahmad

Démo

La solution ci - dessus assing thispour thatensuite nous pouvons et l' accès la propriété de nom dans la sayHiméthode à partir that, donc cela peut être appelé sans problèmes à l' intérieur de l'appel DOM.

Une autre solution consiste à lui affecter un thatobjet vide et à lui ajouter des propriétés et des méthodes, puis à le renvoyer. Mais avec cette solution, vous avez perdu prototypele constructeur.

var Person = function(name) {
  var that = {};
  that.name = name;
  that.sayHi = function() {
    alert(that.name);
  };
  return that;
};
Ahmad Ajmi
la source
2

Voici un exemple `

$(document).ready(function() {
        var lastItem = null;
        $(".our-work-group > p > a").click(function(e) {
            e.preventDefault();

            var item = $(this).html(); //Here value of "this" is ".our-work-group > p > a"
            if (item == lastItem) {
                lastItem = null;
                $('.our-work-single-page').show();
            } else {
                lastItem = item;
                $('.our-work-single-page').each(function() {
                    var imgAlt = $(this).find('img').attr('alt'); //Here value of "this" is '.our-work-single-page'. 
                    if (imgAlt != item) {
                        $(this).hide();
                    } else {
                        $(this).show();
                    }
                });
            }

        });
    });`

Ainsi, vous pouvez voir que la valeur de this correspond à deux valeurs différentes selon l'élément DOM que vous ciblez, mais lorsque vous ajoutez "that" au code ci-dessus, vous modifiez la valeur de "this" que vous ciblez.

`$(document).ready(function() {
        var lastItem = null;
        $(".our-work-group > p > a").click(function(e) {
            e.preventDefault();
            var item = $(this).html(); //Here value of "this" is ".our-work-group > p > a"
            if (item == lastItem) {
                lastItem = null;
                var that = this;
                $('.our-work-single-page').show();
            } else {
                lastItem = item;
                $('.our-work-single-page').each(function() {
                   ***$(that).css("background-color", "#ffe700");*** //Here value of "that" is ".our-work-group > p > a"....
                    var imgAlt = $(this).find('img').attr('alt'); 
                    if (imgAlt != item) {
                        $(this).hide();
                    } else {
                        $(this).show();
                    }
                });
            }

        });
    });`

..... $ (that) .css ("background-color", "# ffe700"); // Ici, la valeur de "that" est ".our-work-group> p> a" car la valeur de var that = this; donc même si nous sommes à "this" = '.our-work-single-page', nous pouvons toujours utiliser "that" pour manipuler l'élément DOM précédent.

stacksonstacksonstacks
la source